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

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

DEFINITIONS

This source file includes following definitions.
  1. glwthread_waitqueue_init
  2. glwthread_waitqueue_add
  3. glwthread_waitqueue_remove
  4. glwthread_waitqueue_notify_first
  5. glwthread_waitqueue_notify_all
  6. glwthread_cond_init
  7. glwthread_cond_wait
  8. glwthread_cond_timedwait
  9. glwthread_cond_signal
  10. glwthread_cond_broadcast
  11. glwthread_cond_destroy

   1 /* Condition variables (native Windows implementation).
   2    Copyright (C) 2008-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 Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
  18    and Bruno Haible <bruno@clisp.org>, 2008.  */
  19 
  20 #include <config.h>
  21 
  22 /* Specification.  */
  23 #include "windows-cond.h"
  24 
  25 #include <errno.h>
  26 #include <stdbool.h>
  27 #include <stdlib.h>
  28 #include <sys/time.h>
  29 
  30 /* Don't assume that UNICODE is not defined.  */
  31 #undef CreateEvent
  32 #define CreateEvent CreateEventA
  33 
  34 /* In this file, the waitqueues are implemented as linked lists.  */
  35 #define glwthread_waitqueue_t glwthread_linked_waitqueue_t
  36 
  37 /* All links of a circular list, except the anchor, are of this type, carrying
  38    a payload.  */
  39 struct glwthread_waitqueue_element
  40 {
  41   struct glwthread_waitqueue_link link; /* must be the first field! */
  42   HANDLE event; /* Waiting thread, represented by an event.
  43                    This field is immutable once initialized. */
  44 };
  45 
  46 static void
  47 glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49   wq->wq_list.wql_next = &wq->wq_list;
  50   wq->wq_list.wql_prev = &wq->wq_list;
  51 }
  52 
  53 /* Enqueues the current thread, represented by an event, in a wait queue.
  54    Returns NULL if an allocation failure occurs.  */
  55 static struct glwthread_waitqueue_element *
  56 glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
     /* [previous][next][first][last][top][bottom][index][help] */
  57 {
  58   struct glwthread_waitqueue_element *elt;
  59   HANDLE event;
  60 
  61   /* Allocate the memory for the waitqueue element on the heap, not on the
  62      thread's stack.  If the thread exits unexpectedly, we prefer to leak
  63      some memory rather than to access unavailable memory and crash.  */
  64   elt =
  65     (struct glwthread_waitqueue_element *)
  66     malloc (sizeof (struct glwthread_waitqueue_element));
  67   if (elt == NULL)
  68     /* No more memory.  */
  69     return NULL;
  70 
  71   /* Whether the created event is a manual-reset one or an auto-reset one,
  72      does not matter, since we will wait on it only once.  */
  73   event = CreateEvent (NULL, TRUE, FALSE, NULL);
  74   if (event == INVALID_HANDLE_VALUE)
  75     {
  76       /* No way to allocate an event.  */
  77       free (elt);
  78       return NULL;
  79     }
  80   elt->event = event;
  81   /* Insert elt at the end of the circular list.  */
  82   (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
  83   (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
  84   return elt;
  85 }
  86 
  87 /* Removes the current thread, represented by a
  88    'struct glwthread_waitqueue_element *', from a wait queue.
  89    Returns true if is was found and removed, false if it was not present.  */
  90 static bool
  91 glwthread_waitqueue_remove (glwthread_waitqueue_t *wq,
     /* [previous][next][first][last][top][bottom][index][help] */
  92                             struct glwthread_waitqueue_element *elt)
  93 {
  94   if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
  95     {
  96       /* Remove elt from the circular list.  */
  97       struct glwthread_waitqueue_link *prev = elt->link.wql_prev;
  98       struct glwthread_waitqueue_link *next = elt->link.wql_next;
  99       prev->wql_next = next;
 100       next->wql_prev = prev;
 101       elt->link.wql_next = NULL;
 102       elt->link.wql_prev = NULL;
 103       return true;
 104     }
 105   else
 106     return false;
 107 }
 108 
 109 /* Notifies the first thread from a wait queue and dequeues it.  */
 110 static void
 111 glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113   if (wq->wq_list.wql_next != &wq->wq_list)
 114     {
 115       struct glwthread_waitqueue_element *elt =
 116         (struct glwthread_waitqueue_element *) wq->wq_list.wql_next;
 117       struct glwthread_waitqueue_link *prev;
 118       struct glwthread_waitqueue_link *next;
 119 
 120       /* Remove elt from the circular list.  */
 121       prev = &wq->wq_list; /* = elt->link.wql_prev; */
 122       next = elt->link.wql_next;
 123       prev->wql_next = next;
 124       next->wql_prev = prev;
 125       elt->link.wql_next = NULL;
 126       elt->link.wql_prev = NULL;
 127 
 128       SetEvent (elt->event);
 129       /* After the SetEvent, this thread cannot access *elt any more, because
 130          the woken-up thread will quickly call  free (elt).  */
 131     }
 132 }
 133 
 134 /* Notifies all threads from a wait queue and dequeues them all.  */
 135 static void
 136 glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138   struct glwthread_waitqueue_link *l;
 139 
 140   for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
 141     {
 142       struct glwthread_waitqueue_element *elt =
 143         (struct glwthread_waitqueue_element *) l;
 144       struct glwthread_waitqueue_link *prev;
 145       struct glwthread_waitqueue_link *next;
 146 
 147       /* Remove elt from the circular list.  */
 148       prev = &wq->wq_list; /* = elt->link.wql_prev; */
 149       next = elt->link.wql_next;
 150       prev->wql_next = next;
 151       next->wql_prev = prev;
 152       elt->link.wql_next = NULL;
 153       elt->link.wql_prev = NULL;
 154 
 155       SetEvent (elt->event);
 156       /* After the SetEvent, this thread cannot access *elt any more, because
 157          the woken-up thread will quickly call  free (elt).  */
 158 
 159       l = next;
 160     }
 161   if (!(wq->wq_list.wql_next == &wq->wq_list
 162         && wq->wq_list.wql_prev == &wq->wq_list))
 163     abort ();
 164 }
 165 
 166 int
 167 glwthread_cond_init (glwthread_cond_t *cond)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169   InitializeCriticalSection (&cond->lock);
 170   glwthread_waitqueue_init (&cond->waiters);
 171 
 172   cond->guard.done = 1;
 173   return 0;
 174 }
 175 
 176 int
 177 glwthread_cond_wait (glwthread_cond_t *cond,
     /* [previous][next][first][last][top][bottom][index][help] */
 178                      void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *))
 179 {
 180   if (!cond->guard.done)
 181     {
 182       if (InterlockedIncrement (&cond->guard.started) == 0)
 183         /* This thread is the first one to need this condition variable.
 184            Initialize it.  */
 185         glwthread_cond_init (cond);
 186       else
 187         {
 188           /* Don't let cond->guard.started grow and wrap around.  */
 189           InterlockedDecrement (&cond->guard.started);
 190           /* Yield the CPU while waiting for another thread to finish
 191              initializing this condition variable.  */
 192           while (!cond->guard.done)
 193             Sleep (0);
 194         }
 195     }
 196 
 197   EnterCriticalSection (&cond->lock);
 198   {
 199     struct glwthread_waitqueue_element *elt =
 200       glwthread_waitqueue_add (&cond->waiters);
 201     LeaveCriticalSection (&cond->lock);
 202     if (elt == NULL)
 203       {
 204         /* Allocation failure.  Weird.  */
 205         return EAGAIN;
 206       }
 207     else
 208       {
 209         HANDLE event = elt->event;
 210         int err;
 211         DWORD result;
 212 
 213         /* Now release the mutex and let any other thread take it.  */
 214         err = mutex_unlock (mutex);
 215         if (err != 0)
 216           {
 217             EnterCriticalSection (&cond->lock);
 218             glwthread_waitqueue_remove (&cond->waiters, elt);
 219             LeaveCriticalSection (&cond->lock);
 220             CloseHandle (event);
 221             free (elt);
 222             return err;
 223           }
 224         /* POSIX says:
 225             "If another thread is able to acquire the mutex after the
 226              about-to-block thread has released it, then a subsequent call to
 227              pthread_cond_broadcast() or pthread_cond_signal() in that thread
 228              shall behave as if it were issued after the about-to-block thread
 229              has blocked."
 230            This is fulfilled here, because the thread signalling is done
 231            through SetEvent, not PulseEvent.  */
 232         /* Wait until another thread signals this event.  */
 233         result = WaitForSingleObject (event, INFINITE);
 234         if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
 235           abort ();
 236         CloseHandle (event);
 237         free (elt);
 238         /* The thread which signalled the event already did the bookkeeping:
 239            removed us from the waiters.  */
 240         return mutex_lock (mutex);
 241       }
 242   }
 243 }
 244 
 245 int
 246 glwthread_cond_timedwait (glwthread_cond_t *cond,
     /* [previous][next][first][last][top][bottom][index][help] */
 247                           void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *),
 248                           const struct timespec *abstime)
 249 {
 250   if (!cond->guard.done)
 251     {
 252       if (InterlockedIncrement (&cond->guard.started) == 0)
 253         /* This thread is the first one to need this condition variable.
 254            Initialize it.  */
 255         glwthread_cond_init (cond);
 256       else
 257         {
 258           /* Don't let cond->guard.started grow and wrap around.  */
 259           InterlockedDecrement (&cond->guard.started);
 260           /* Yield the CPU while waiting for another thread to finish
 261              initializing this condition variable.  */
 262           while (!cond->guard.done)
 263             Sleep (0);
 264         }
 265     }
 266 
 267   {
 268     struct timeval currtime;
 269 
 270     gettimeofday (&currtime, NULL);
 271     if (currtime.tv_sec > abstime->tv_sec
 272         || (currtime.tv_sec == abstime->tv_sec
 273             && currtime.tv_usec * 1000 >= abstime->tv_nsec))
 274       return ETIMEDOUT;
 275 
 276     EnterCriticalSection (&cond->lock);
 277     {
 278       struct glwthread_waitqueue_element *elt =
 279         glwthread_waitqueue_add (&cond->waiters);
 280       LeaveCriticalSection (&cond->lock);
 281       if (elt == NULL)
 282         {
 283           /* Allocation failure.  Weird.  */
 284           return EAGAIN;
 285         }
 286       else
 287         {
 288           HANDLE event = elt->event;
 289           int err;
 290           DWORD timeout;
 291           DWORD result;
 292 
 293           /* Now release the mutex and let any other thread take it.  */
 294           err = mutex_unlock (mutex);
 295           if (err != 0)
 296             {
 297               EnterCriticalSection (&cond->lock);
 298               glwthread_waitqueue_remove (&cond->waiters, elt);
 299               LeaveCriticalSection (&cond->lock);
 300               CloseHandle (event);
 301               free (elt);
 302               return err;
 303             }
 304           /* POSIX says:
 305               "If another thread is able to acquire the mutex after the
 306                about-to-block thread has released it, then a subsequent call to
 307                pthread_cond_broadcast() or pthread_cond_signal() in that thread
 308                shall behave as if it were issued after the about-to-block thread
 309                has blocked."
 310              This is fulfilled here, because the thread signalling is done
 311              through SetEvent, not PulseEvent.  */
 312           /* Wait until another thread signals this event or until the abstime
 313              passes.  */
 314           gettimeofday (&currtime, NULL);
 315           if (currtime.tv_sec > abstime->tv_sec)
 316             timeout = 0;
 317           else
 318             {
 319               unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
 320               timeout = seconds * 1000;
 321               if (timeout / 1000 != seconds) /* overflow? */
 322                 timeout = INFINITE;
 323               else
 324                 {
 325                   long milliseconds =
 326                     abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
 327                   if (milliseconds >= 0)
 328                     {
 329                       timeout += milliseconds;
 330                       if (timeout < milliseconds) /* overflow? */
 331                         timeout = INFINITE;
 332                     }
 333                   else
 334                     {
 335                       if (timeout >= - milliseconds)
 336                         timeout -= (- milliseconds);
 337                       else
 338                         timeout = 0;
 339                     }
 340                 }
 341             }
 342           result = WaitForSingleObject (event, timeout);
 343           if (result == WAIT_FAILED)
 344             abort ();
 345           if (result == WAIT_TIMEOUT)
 346             {
 347               EnterCriticalSection (&cond->lock);
 348               if (glwthread_waitqueue_remove (&cond->waiters, elt))
 349                 {
 350                   /* The event was not signaled between the WaitForSingleObject
 351                      call and the EnterCriticalSection call.  */
 352                   if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
 353                     abort ();
 354                 }
 355               else
 356                 {
 357                   /* The event was signaled between the WaitForSingleObject
 358                      call and the EnterCriticalSection call.  */
 359                   if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
 360                     abort ();
 361                   /* Produce the right return value.  */
 362                   result = WAIT_OBJECT_0;
 363                 }
 364               LeaveCriticalSection (&cond->lock);
 365             }
 366           else
 367             {
 368               /* The thread which signalled the event already did the
 369                  bookkeeping: removed us from the waiters.  */
 370             }
 371           CloseHandle (event);
 372           free (elt);
 373           /* Take the mutex again.  It does not matter whether this is done
 374              before or after the bookkeeping for WAIT_TIMEOUT.  */
 375           err = mutex_lock (mutex);
 376           return (err ? err :
 377                   result == WAIT_OBJECT_0 ? 0 :
 378                   result == WAIT_TIMEOUT ? ETIMEDOUT :
 379                   /* WAIT_FAILED shouldn't happen */ EAGAIN);
 380         }
 381     }
 382   }
 383 }
 384 
 385 int
 386 glwthread_cond_signal (glwthread_cond_t *cond)
     /* [previous][next][first][last][top][bottom][index][help] */
 387 {
 388   if (!cond->guard.done)
 389     return EINVAL;
 390 
 391   EnterCriticalSection (&cond->lock);
 392   /* POSIX says:
 393       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
 394        have no effect if there are no threads currently blocked on cond."  */
 395   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
 396     glwthread_waitqueue_notify_first (&cond->waiters);
 397   LeaveCriticalSection (&cond->lock);
 398 
 399   return 0;
 400 }
 401 
 402 int
 403 glwthread_cond_broadcast (glwthread_cond_t *cond)
     /* [previous][next][first][last][top][bottom][index][help] */
 404 {
 405   if (!cond->guard.done)
 406     return EINVAL;
 407 
 408   EnterCriticalSection (&cond->lock);
 409   /* POSIX says:
 410       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
 411        have no effect if there are no threads currently blocked on cond."
 412      glwthread_waitqueue_notify_all is a nop in this case.  */
 413   glwthread_waitqueue_notify_all (&cond->waiters);
 414   LeaveCriticalSection (&cond->lock);
 415 
 416   return 0;
 417 }
 418 
 419 int
 420 glwthread_cond_destroy (glwthread_cond_t *cond)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422   if (!cond->guard.done)
 423     return EINVAL;
 424   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
 425     return EBUSY;
 426   DeleteCriticalSection (&cond->lock);
 427   cond->guard.done = 0;
 428   return 0;
 429 }

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