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