(root)/
coreutils-9.4/
lib/
windows-timedrecmutex.c
       1  /* Timed recursive 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-timedrecmutex.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_timedrecmutex_init (glwthread_timedrecmutex_t *mutex)
      35  {
      36    mutex->owner = 0;
      37    mutex->depth = 0;
      38    /* Attempt to allocate an auto-reset event object.  */
      39    /* CreateEvent
      40       <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
      41    HANDLE event = CreateEvent (NULL, FALSE, FALSE, NULL);
      42    if (event == INVALID_HANDLE_VALUE)
      43      return EAGAIN;
      44    mutex->event = event;
      45    InitializeCriticalSection (&mutex->lock);
      46    mutex->guard.done = 1;
      47    return 0;
      48  }
      49  
      50  int
      51  glwthread_timedrecmutex_lock (glwthread_timedrecmutex_t *mutex)
      52  {
      53    if (!mutex->guard.done)
      54      {
      55        if (InterlockedIncrement (&mutex->guard.started) == 0)
      56          {
      57            /* This thread is the first one to need this mutex.
      58               Initialize it.  */
      59            int err = glwthread_timedrecmutex_init (mutex);
      60            if (err != 0)
      61              {
      62                /* Undo increment.  */
      63                InterlockedDecrement (&mutex->guard.started);
      64                return err;
      65              }
      66          }
      67        else
      68          {
      69            /* Don't let mutex->guard.started grow and wrap around.  */
      70            InterlockedDecrement (&mutex->guard.started);
      71            /* Yield the CPU while waiting for another thread to finish
      72               initializing this mutex.  */
      73            while (!mutex->guard.done)
      74              Sleep (0);
      75          }
      76      }
      77    {
      78      DWORD self = GetCurrentThreadId ();
      79      if (mutex->owner != self)
      80        {
      81          EnterCriticalSection (&mutex->lock);
      82          mutex->owner = self;
      83        }
      84      if (++(mutex->depth) == 0) /* wraparound? */
      85        {
      86          mutex->depth--;
      87          return EAGAIN;
      88        }
      89    }
      90    return 0;
      91  }
      92  
      93  int
      94  glwthread_timedrecmutex_trylock (glwthread_timedrecmutex_t *mutex)
      95  {
      96    if (!mutex->guard.done)
      97      {
      98        if (InterlockedIncrement (&mutex->guard.started) == 0)
      99          {
     100            /* This thread is the first one to need this mutex.
     101               Initialize it.  */
     102            int err = glwthread_timedrecmutex_init (mutex);
     103            if (err != 0)
     104              {
     105                /* Undo increment.  */
     106                InterlockedDecrement (&mutex->guard.started);
     107                return err;
     108              }
     109          }
     110        else
     111          {
     112            /* Don't let mutex->guard.started grow and wrap around.  */
     113            InterlockedDecrement (&mutex->guard.started);
     114            /* Let another thread finish initializing this mutex, and let it also
     115               lock this mutex.  */
     116            return EBUSY;
     117          }
     118      }
     119    {
     120      DWORD self = GetCurrentThreadId ();
     121      if (mutex->owner != self)
     122        {
     123          if (!TryEnterCriticalSection (&mutex->lock))
     124            return EBUSY;
     125          mutex->owner = self;
     126        }
     127      if (++(mutex->depth) == 0) /* wraparound? */
     128        {
     129          mutex->depth--;
     130          return EAGAIN;
     131        }
     132    }
     133    return 0;
     134  }
     135  
     136  int
     137  glwthread_timedrecmutex_timedlock (glwthread_timedrecmutex_t *mutex,
     138                                     const struct timespec *abstime)
     139  {
     140    if (!mutex->guard.done)
     141      {
     142        if (InterlockedIncrement (&mutex->guard.started) == 0)
     143          {
     144            /* This thread is the first one to need this mutex.
     145               Initialize it.  */
     146            int err = glwthread_timedrecmutex_init (mutex);
     147            if (err != 0)
     148              {
     149                /* Undo increment.  */
     150                InterlockedDecrement (&mutex->guard.started);
     151                return err;
     152              }
     153          }
     154        else
     155          {
     156            /* Don't let mutex->guard.started grow and wrap around.  */
     157            InterlockedDecrement (&mutex->guard.started);
     158            /* Yield the CPU while waiting for another thread to finish
     159               initializing this mutex.  */
     160            while (!mutex->guard.done)
     161              Sleep (0);
     162          }
     163      }
     164  
     165    {
     166      DWORD self = GetCurrentThreadId ();
     167      if (mutex->owner != self)
     168        {
     169          /* POSIX says:
     170              "Under no circumstance shall the function fail with a timeout if
     171               the mutex can be locked immediately. The validity of the abstime
     172               parameter need not be checked if the mutex can be locked
     173               immediately."
     174             Therefore start the loop with a TryEnterCriticalSection call.  */
     175          for (;;)
     176            {
     177              if (TryEnterCriticalSection (&mutex->lock))
     178                break;
     179  
     180              {
     181                struct timeval currtime;
     182                DWORD timeout;
     183                DWORD result;
     184  
     185                gettimeofday (&currtime, NULL);
     186  
     187                /* Wait until another thread signals the event or until the
     188                   abstime passes.  */
     189                if (currtime.tv_sec > abstime->tv_sec)
     190                  timeout = 0;
     191                else
     192                  {
     193                    unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
     194                    timeout = seconds * 1000;
     195                    if (timeout / 1000 != seconds) /* overflow? */
     196                      timeout = INFINITE;
     197                    else
     198                      {
     199                        long milliseconds =
     200                          abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
     201                        if (milliseconds >= 0)
     202                          {
     203                            timeout += milliseconds;
     204                            if (timeout < milliseconds) /* overflow? */
     205                              timeout = INFINITE;
     206                          }
     207                        else
     208                          {
     209                            if (timeout >= - milliseconds)
     210                              timeout -= (- milliseconds);
     211                            else
     212                              timeout = 0;
     213                          }
     214                      }
     215                  }
     216                if (timeout == 0)
     217                  return ETIMEDOUT;
     218  
     219                /* WaitForSingleObject
     220                   <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
     221                result = WaitForSingleObject (mutex->event, timeout);
     222                if (result == WAIT_FAILED)
     223                  abort ();
     224                if (result == WAIT_TIMEOUT)
     225                  return ETIMEDOUT;
     226                /* Another thread has just unlocked the mutex.  We have good chances at
     227                   locking it now.  */
     228              }
     229            }
     230          mutex->owner = self;
     231        }
     232      if (++(mutex->depth) == 0) /* wraparound? */
     233        {
     234          mutex->depth--;
     235          return EAGAIN;
     236        }
     237    }
     238    return 0;
     239  }
     240  
     241  int
     242  glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t *mutex)
     243  {
     244    if (mutex->owner != GetCurrentThreadId ())
     245      return EPERM;
     246    if (mutex->depth == 0)
     247      return EINVAL;
     248    if (--(mutex->depth) == 0)
     249      {
     250        mutex->owner = 0;
     251        LeaveCriticalSection (&mutex->lock);
     252        /* Notify one of the threads that were waiting with a timeout.  */
     253        /* SetEvent
     254           <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
     255        SetEvent (mutex->event);
     256      }
     257    return 0;
     258  }
     259  
     260  int
     261  glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t *mutex)
     262  {
     263    if (mutex->owner != 0)
     264      return EBUSY;
     265    DeleteCriticalSection (&mutex->lock);
     266    /* CloseHandle
     267       <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
     268    CloseHandle (mutex->event);
     269    mutex->guard.done = 0;
     270    return 0;
     271  }