maint: update copyright
[gnulib.git] / lib / glthread / cond.c
1 /* Condition variables for multithreading.
2    Copyright (C) 2008-2014 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program 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 General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://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 #define _GLTHREAD_COND_INLINE _GL_EXTERN_INLINE
23 #include "glthread/cond.h"
24
25 /* ========================================================================= */
26
27 #if USE_PTH_THREADS
28
29 /* -------------------------- gl_cond_t datatype -------------------------- */
30
31 int
32 glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
33                                        gl_lock_t *lock,
34                                        struct timespec *abstime)
35 {
36   int ret, status;
37   pth_event_t ev;
38
39   ev = pth_event (PTH_EVENT_TIME, pth_time (abstime->tv_sec, abstime->tv_nsec / 1000));
40   ret = pth_cond_await (cond, lock, ev);
41
42   status = pth_event_status (ev);
43   pth_event_free (ev, PTH_FREE_THIS);
44
45   if (status == PTH_STATUS_OCCURRED)
46     return ETIMEDOUT;
47
48   return ret;
49 }
50
51 #endif
52
53 /* ========================================================================= */
54
55 #if USE_SOLARIS_THREADS
56
57 /* -------------------------- gl_cond_t datatype -------------------------- */
58
59 int
60 glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
61                                        gl_lock_t *lock,
62                                        struct timespec *abstime)
63 {
64   int ret;
65
66   ret = cond_timedwait (cond, lock, abstime);
67   if (ret == ETIME)
68     return ETIMEDOUT;
69   return ret;
70 }
71
72 #endif
73
74 /* ========================================================================= */
75
76 #if USE_WINDOWS_THREADS
77
78 #include <sys/time.h>
79
80 /* -------------------------- gl_cond_t datatype -------------------------- */
81
82 /* In this file, the waitqueues are implemented as linked lists.  */
83 #define gl_waitqueue_t gl_linked_waitqueue_t
84
85 /* All links of a circular list, except the anchor, are of this type, carrying
86    a payload.  */
87 struct gl_waitqueue_element
88 {
89   struct gl_waitqueue_link link; /* must be the first field! */
90   HANDLE event; /* Waiting thread, represented by an event.
91                    This field is immutable once initialized. */
92 };
93
94 static void
95 gl_waitqueue_init (gl_waitqueue_t *wq)
96 {
97   wq->wq_list.wql_next = &wq->wq_list;
98   wq->wq_list.wql_prev = &wq->wq_list;
99 }
100
101 /* Enqueues the current thread, represented by an event, in a wait queue.
102    Returns NULL if an allocation failure occurs.  */
103 static struct gl_waitqueue_element *
104 gl_waitqueue_add (gl_waitqueue_t *wq)
105 {
106   struct gl_waitqueue_element *elt;
107   HANDLE event;
108
109   /* Allocate the memory for the waitqueue element on the heap, not on the
110      thread's stack.  If the thread exits unexpectedly, we prefer to leak
111      some memory rather than to access unavailable memory and crash.  */
112   elt =
113     (struct gl_waitqueue_element *)
114     malloc (sizeof (struct gl_waitqueue_element));
115   if (elt == NULL)
116     /* No more memory.  */
117     return NULL;
118
119   /* Whether the created event is a manual-reset one or an auto-reset one,
120      does not matter, since we will wait on it only once.  */
121   event = CreateEvent (NULL, TRUE, FALSE, NULL);
122   if (event == INVALID_HANDLE_VALUE)
123     {
124       /* No way to allocate an event.  */
125       free (elt);
126       return NULL;
127     }
128   elt->event = event;
129   /* Insert elt at the end of the circular list.  */
130   (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
131   (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
132   return elt;
133 }
134
135 /* Removes the current thread, represented by a 'struct gl_waitqueue_element *',
136    from a wait queue.
137    Returns true if is was found and removed, false if it was not present.  */
138 static bool
139 gl_waitqueue_remove (gl_waitqueue_t *wq, struct gl_waitqueue_element *elt)
140 {
141   if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
142     {
143       /* Remove elt from the circular list.  */
144       struct gl_waitqueue_link *prev = elt->link.wql_prev;
145       struct gl_waitqueue_link *next = elt->link.wql_next;
146       prev->wql_next = next;
147       next->wql_prev = prev;
148       elt->link.wql_next = NULL;
149       elt->link.wql_prev = NULL;
150       return true;
151     }
152   else
153     return false;
154 }
155
156 /* Notifies the first thread from a wait queue and dequeues it.  */
157 static void
158 gl_waitqueue_notify_first (gl_waitqueue_t *wq)
159 {
160   if (wq->wq_list.wql_next != &wq->wq_list)
161     {
162       struct gl_waitqueue_element *elt =
163         (struct gl_waitqueue_element *) wq->wq_list.wql_next;
164       struct gl_waitqueue_link *prev;
165       struct gl_waitqueue_link *next;
166
167       /* Remove elt from the circular list.  */
168       prev = &wq->wq_list; /* = elt->link.wql_prev; */
169       next = elt->link.wql_next;
170       prev->wql_next = next;
171       next->wql_prev = prev;
172       elt->link.wql_next = NULL;
173       elt->link.wql_prev = NULL;
174
175       SetEvent (elt->event);
176       /* After the SetEvent, this thread cannot access *elt any more, because
177          the woken-up thread will quickly call  free (elt).  */
178     }
179 }
180
181 /* Notifies all threads from a wait queue and dequeues them all.  */
182 static void
183 gl_waitqueue_notify_all (gl_waitqueue_t *wq)
184 {
185   struct gl_waitqueue_link *l;
186
187   for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
188     {
189       struct gl_waitqueue_element *elt = (struct gl_waitqueue_element *) l;
190       struct gl_waitqueue_link *prev;
191       struct gl_waitqueue_link *next;
192
193       /* Remove elt from the circular list.  */
194       prev = &wq->wq_list; /* = elt->link.wql_prev; */
195       next = elt->link.wql_next;
196       prev->wql_next = next;
197       next->wql_prev = prev;
198       elt->link.wql_next = NULL;
199       elt->link.wql_prev = NULL;
200
201       SetEvent (elt->event);
202       /* After the SetEvent, this thread cannot access *elt any more, because
203          the woken-up thread will quickly call  free (elt).  */
204
205       l = next;
206     }
207   if (!(wq->wq_list.wql_next == &wq->wq_list
208         && wq->wq_list.wql_prev == &wq->wq_list))
209     abort ();
210 }
211
212 int
213 glthread_cond_init_func (gl_cond_t *cond)
214 {
215   InitializeCriticalSection (&cond->lock);
216   gl_waitqueue_init (&cond->waiters);
217
218   cond->guard.done = 1;
219   return 0;
220 }
221
222 int
223 glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
224 {
225   if (!cond->guard.done)
226     {
227       if (InterlockedIncrement (&cond->guard.started) == 0)
228         /* This thread is the first one to need this condition variable.
229            Initialize it.  */
230         glthread_cond_init (cond);
231       else
232         /* Yield the CPU while waiting for another thread to finish
233            initializing this condition variable.  */
234         while (!cond->guard.done)
235           Sleep (0);
236     }
237
238   EnterCriticalSection (&cond->lock);
239   {
240     struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
241     LeaveCriticalSection (&cond->lock);
242     if (elt == NULL)
243       {
244         /* Allocation failure.  Weird.  */
245         return EAGAIN;
246       }
247     else
248       {
249         HANDLE event = elt->event;
250         int err;
251         DWORD result;
252
253         /* Now release the lock and let any other thread take it.  */
254         err = glthread_lock_unlock (lock);
255         if (err != 0)
256           {
257             EnterCriticalSection (&cond->lock);
258             gl_waitqueue_remove (&cond->waiters, elt);
259             LeaveCriticalSection (&cond->lock);
260             CloseHandle (event);
261             free (elt);
262             return err;
263           }
264         /* POSIX says:
265             "If another thread is able to acquire the mutex after the
266              about-to-block thread has released it, then a subsequent call to
267              pthread_cond_broadcast() or pthread_cond_signal() in that thread
268              shall behave as if it were issued after the about-to-block thread
269              has blocked."
270            This is fulfilled here, because the thread signalling is done
271            through SetEvent, not PulseEvent.  */
272         /* Wait until another thread signals this event.  */
273         result = WaitForSingleObject (event, INFINITE);
274         if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
275           abort ();
276         CloseHandle (event);
277         free (elt);
278         /* The thread which signalled the event already did the bookkeeping:
279            removed us from the waiters.  */
280         return glthread_lock_lock (lock);
281       }
282   }
283 }
284
285 int
286 glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
287 {
288   struct timeval currtime;
289
290   gettimeofday (&currtime, NULL);
291   if (currtime.tv_sec > abstime->tv_sec
292       || (currtime.tv_sec == abstime->tv_sec
293           && currtime.tv_usec * 1000 >= abstime->tv_nsec))
294     return ETIMEDOUT;
295
296   if (!cond->guard.done)
297     {
298       if (InterlockedIncrement (&cond->guard.started) == 0)
299         /* This thread is the first one to need this condition variable.
300            Initialize it.  */
301         glthread_cond_init (cond);
302       else
303         /* Yield the CPU while waiting for another thread to finish
304            initializing this condition variable.  */
305         while (!cond->guard.done)
306           Sleep (0);
307     }
308
309   EnterCriticalSection (&cond->lock);
310   {
311     struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
312     LeaveCriticalSection (&cond->lock);
313     if (elt == NULL)
314       {
315         /* Allocation failure.  Weird.  */
316         return EAGAIN;
317       }
318     else
319       {
320         HANDLE event = elt->event;
321         int err;
322         DWORD timeout;
323         DWORD result;
324
325         /* Now release the lock and let any other thread take it.  */
326         err = glthread_lock_unlock (lock);
327         if (err != 0)
328           {
329             EnterCriticalSection (&cond->lock);
330             gl_waitqueue_remove (&cond->waiters, elt);
331             LeaveCriticalSection (&cond->lock);
332             CloseHandle (event);
333             free (elt);
334             return err;
335           }
336         /* POSIX says:
337             "If another thread is able to acquire the mutex after the
338              about-to-block thread has released it, then a subsequent call to
339              pthread_cond_broadcast() or pthread_cond_signal() in that thread
340              shall behave as if it were issued after the about-to-block thread
341              has blocked."
342            This is fulfilled here, because the thread signalling is done
343            through SetEvent, not PulseEvent.  */
344         /* Wait until another thread signals this event or until the abstime
345            passes.  */
346         gettimeofday (&currtime, NULL);
347         if (currtime.tv_sec > abstime->tv_sec)
348           timeout = 0;
349         else
350           {
351             unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
352             timeout = seconds * 1000;
353             if (timeout / 1000 != seconds) /* overflow? */
354               timeout = INFINITE;
355             else
356               {
357                 long milliseconds =
358                   abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
359                 if (milliseconds >= 0)
360                   {
361                     timeout += milliseconds;
362                     if (timeout < milliseconds) /* overflow? */
363                       timeout = INFINITE;
364                   }
365                 else
366                   {
367                     if (timeout >= - milliseconds)
368                       timeout -= (- milliseconds);
369                     else
370                       timeout = 0;
371                   }
372               }
373           }
374         result = WaitForSingleObject (event, timeout);
375         if (result == WAIT_FAILED)
376           abort ();
377         if (result == WAIT_TIMEOUT)
378           {
379             EnterCriticalSection (&cond->lock);
380             if (gl_waitqueue_remove (&cond->waiters, elt))
381               {
382                 /* The event was not signaled between the WaitForSingleObject
383                    call and the EnterCriticalSection call.  */
384                 if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
385                   abort ();
386               }
387             else
388               {
389                 /* The event was signaled between the WaitForSingleObject
390                    call and the EnterCriticalSection call.  */
391                 if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
392                   abort ();
393                 /* Produce the right return value.  */
394                 result = WAIT_OBJECT_0;
395               }
396             LeaveCriticalSection (&cond->lock);
397           }
398         else
399           {
400             /* The thread which signalled the event already did the
401                bookkeeping: removed us from the waiters.  */
402           }
403         CloseHandle (event);
404         free (elt);
405         /* Take the lock again.  It does not matter whether this is done
406            before or after the bookkeeping for WAIT_TIMEOUT.  */
407         err = glthread_lock_lock (lock);
408         return (err ? err :
409                 result == WAIT_OBJECT_0 ? 0 :
410                 result == WAIT_TIMEOUT ? ETIMEDOUT :
411                 /* WAIT_FAILED shouldn't happen */ EAGAIN);
412       }
413   }
414 }
415
416 int
417 glthread_cond_signal_func (gl_cond_t *cond)
418 {
419   if (!cond->guard.done)
420     return EINVAL;
421
422   EnterCriticalSection (&cond->lock);
423   /* POSIX says:
424       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
425        have no effect if there are no threads currently blocked on cond."  */
426   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
427     gl_waitqueue_notify_first (&cond->waiters);
428   LeaveCriticalSection (&cond->lock);
429
430   return 0;
431 }
432
433 int
434 glthread_cond_broadcast_func (gl_cond_t *cond)
435 {
436   if (!cond->guard.done)
437     return EINVAL;
438
439   EnterCriticalSection (&cond->lock);
440   /* POSIX says:
441       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
442        have no effect if there are no threads currently blocked on cond."
443      gl_waitqueue_notify_all is a nop in this case.  */
444   gl_waitqueue_notify_all (&cond->waiters);
445   LeaveCriticalSection (&cond->lock);
446
447   return 0;
448 }
449
450 int
451 glthread_cond_destroy_func (gl_cond_t *cond)
452 {
453   if (!cond->guard.done)
454     return EINVAL;
455   if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
456     return EBUSY;
457   DeleteCriticalSection (&cond->lock);
458   cond->guard.done = 0;
459   return 0;
460 }
461
462 #endif
463
464 /* ========================================================================= */