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

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

DEFINITIONS

This source file includes following definitions.
  1. glwthread_timedmutex_init
  2. glwthread_timedmutex_lock
  3. glwthread_timedmutex_trylock
  4. glwthread_timedmutex_timedlock
  5. glwthread_timedmutex_unlock
  6. glwthread_timedmutex_destroy

   1 /* Timed 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-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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

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