(root)/
coreutils-9.4/
lib/
windows-timedmutex.c
       1  /* Timed mutexes (native Windows implementation).
       2     Copyright (C) 2005-2023 Free Software Foundation, Inc.
       3  
       4     This file is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
      18     Based on GCC's gthr-win32.h.  */
      19  
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include "windows-timedmutex.h"
      24  
      25  #include <errno.h>
      26  #include <stdlib.h>
      27  #include <sys/time.h>
      28  
      29  /* Don't assume that UNICODE is not defined.  */
      30  #undef CreateEvent
      31  #define CreateEvent CreateEventA
      32  
      33  int
      34  glwthread_timedmutex_init (glwthread_timedmutex_t *mutex)
      35  {
      36    /* Attempt to allocate an auto-reset event object.  */
      37    /* CreateEvent
      38       <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
      39    HANDLE event = CreateEvent (NULL, FALSE, FALSE, NULL);
      40    if (event == INVALID_HANDLE_VALUE)
      41      return EAGAIN;
      42    mutex->event = event;
      43    InitializeCriticalSection (&mutex->lock);
      44    mutex->guard.done = 1;
      45    return 0;
      46  }
      47  
      48  int
      49  glwthread_timedmutex_lock (glwthread_timedmutex_t *mutex)
      50  {
      51    if (!mutex->guard.done)
      52      {
      53        if (InterlockedIncrement (&mutex->guard.started) == 0)
      54          {
      55            /* This thread is the first one to need this mutex.
      56               Initialize it.  */
      57            int err = glwthread_timedmutex_init (mutex);
      58            if (err != 0)
      59              {
      60                /* Undo increment.  */
      61                InterlockedDecrement (&mutex->guard.started);
      62                return err;
      63              }
      64          }
      65        else
      66          {
      67            /* Don't let mutex->guard.started grow and wrap around.  */
      68            InterlockedDecrement (&mutex->guard.started);
      69            /* Yield the CPU while waiting for another thread to finish
      70               initializing this mutex.  */
      71            while (!mutex->guard.done)
      72              Sleep (0);
      73          }
      74      }
      75    EnterCriticalSection (&mutex->lock);
      76    return 0;
      77  }
      78  
      79  int
      80  glwthread_timedmutex_trylock (glwthread_timedmutex_t *mutex)
      81  {
      82    if (!mutex->guard.done)
      83      {
      84        if (InterlockedIncrement (&mutex->guard.started) == 0)
      85          {
      86            /* This thread is the first one to need this mutex.
      87               Initialize it.  */
      88            int err = glwthread_timedmutex_init (mutex);
      89            if (err != 0)
      90              {
      91                /* Undo increment.  */
      92                InterlockedDecrement (&mutex->guard.started);
      93                return err;
      94              }
      95          }
      96        else
      97          {
      98            /* Don't let mutex->guard.started grow and wrap around.  */
      99            InterlockedDecrement (&mutex->guard.started);
     100            /* Let another thread finish initializing this mutex, and let it also
     101               lock this mutex.  */
     102            return EBUSY;
     103          }
     104      }
     105    if (!TryEnterCriticalSection (&mutex->lock))
     106      return EBUSY;
     107    return 0;
     108  }
     109  
     110  int
     111  glwthread_timedmutex_timedlock (glwthread_timedmutex_t *mutex,
     112                                  const struct timespec *abstime)
     113  {
     114    if (!mutex->guard.done)
     115      {
     116        if (InterlockedIncrement (&mutex->guard.started) == 0)
     117          {
     118            /* This thread is the first one to need this mutex.
     119               Initialize it.  */
     120            int err = glwthread_timedmutex_init (mutex);
     121            if (err != 0)
     122              {
     123                /* Undo increment.  */
     124                InterlockedDecrement (&mutex->guard.started);
     125                return err;
     126              }
     127          }
     128        else
     129          {
     130            /* Don't let mutex->guard.started grow and wrap around.  */
     131            InterlockedDecrement (&mutex->guard.started);
     132            /* Yield the CPU while waiting for another thread to finish
     133               initializing this mutex.  */
     134            while (!mutex->guard.done)
     135              Sleep (0);
     136          }
     137      }
     138  
     139    /* POSIX says:
     140        "Under no circumstance shall the function fail with a timeout if
     141         the mutex can be locked immediately. The validity of the abstime
     142         parameter need not be checked if the mutex can be locked
     143         immediately."
     144       Therefore start the loop with a TryEnterCriticalSection call.  */
     145    for (;;)
     146      {
     147        if (TryEnterCriticalSection (&mutex->lock))
     148          break;
     149  
     150        {
     151          struct timeval currtime;
     152          DWORD timeout;
     153          DWORD result;
     154  
     155          gettimeofday (&currtime, NULL);
     156  
     157          /* Wait until another thread signals the event or until the
     158             abstime passes.  */
     159          if (currtime.tv_sec > abstime->tv_sec)
     160            timeout = 0;
     161          else
     162            {
     163              unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
     164              timeout = seconds * 1000;
     165              if (timeout / 1000 != seconds) /* overflow? */
     166                timeout = INFINITE;
     167              else
     168                {
     169                  long milliseconds =
     170                    abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
     171                  if (milliseconds >= 0)
     172                    {
     173                      timeout += milliseconds;
     174                      if (timeout < milliseconds) /* overflow? */
     175                        timeout = INFINITE;
     176                    }
     177                  else
     178                    {
     179                      if (timeout >= - milliseconds)
     180                        timeout -= (- milliseconds);
     181                      else
     182                        timeout = 0;
     183                    }
     184                }
     185            }
     186          if (timeout == 0)
     187            return ETIMEDOUT;
     188  
     189          /* WaitForSingleObject
     190             <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
     191          result = WaitForSingleObject (mutex->event, timeout);
     192          if (result == WAIT_FAILED)
     193            abort ();
     194          if (result == WAIT_TIMEOUT)
     195            return ETIMEDOUT;
     196          /* Another thread has just unlocked the mutex.  We have good chances at
     197             locking it now.  */
     198        }
     199      }
     200    return 0;
     201  }
     202  
     203  int
     204  glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex)
     205  {
     206    if (!mutex->guard.done)
     207      return EINVAL;
     208    LeaveCriticalSection (&mutex->lock);
     209    /* Notify one of the threads that were waiting with a timeout.  */
     210    /* SetEvent
     211       <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
     212    SetEvent (mutex->event);
     213    return 0;
     214  }
     215  
     216  int
     217  glwthread_timedmutex_destroy (glwthread_timedmutex_t *mutex)
     218  {
     219    if (!mutex->guard.done)
     220      return EINVAL;
     221    DeleteCriticalSection (&mutex->lock);
     222    /* CloseHandle
     223       <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
     224    CloseHandle (mutex->event);
     225    mutex->guard.done = 0;
     226    return 0;
     227  }