root/maint/gnulib/lib/windows-timedrecmutex.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. glwthread_timedrecmutex_init
  2. glwthread_timedrecmutex_lock
  3. glwthread_timedrecmutex_trylock
  4. glwthread_timedrecmutex_timedlock
  5. glwthread_timedrecmutex_unlock
  6. glwthread_timedrecmutex_destroy

   1 /* Timed recursive mutexes (native Windows implementation).
   2    Copyright (C) 2005-2021 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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

/* [previous][next][first][last][top][bottom][index][help] */