Fix syntax error in native Win32 code.
[gnulib.git] / lib / glthread / cond.c
1 /* Condition variables for multithreading.
2    Copyright (C) 2008 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, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
19    and Bruno Haible <bruno@clisp.org>, 2008.  */
20
21 #include <config.h>
22
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_WIN32_THREADS
77
78 #include <sys/time.h>
79
80 /* -------------------------- gl_cond_t datatype -------------------------- */
81
82 /* This implementation is based on the article
83    Douglas C. Schmidt, Irfan Pyarali
84    "Strategies for Implementing POSIX Condition Variables on Win32"
85    <http://www.cs.wustl.edu/~schmidt/win32-cv-1.html>  */
86
87 int
88 glthread_cond_init_func (gl_cond_t *cond)
89 {
90   InitializeCriticalSection (&cond->lock);
91   /* Create a manual-reset event.  */
92   cond->event = CreateEvent (NULL, TRUE, FALSE, NULL);
93   cond->waiters_count = 0;
94   cond->release_count = 0;
95   cond->wait_generation_count = 0;
96
97   cond->guard.done = 1;
98
99   return 0;
100 }
101
102 int
103 glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
104 {
105   if (!cond->guard.done)
106     {
107       if (InterlockedIncrement (&cond->guard.started) == 0)
108         /* This thread is the first one to need this condition variable.
109            Initialize it.  */
110         glthread_cond_init (cond);
111       else
112         /* Yield the CPU while waiting for another thread to finish
113            initializing this condition variable.  */
114         while (!cond->guard.done)
115           Sleep (0);
116     }
117
118   {
119     unsigned old_generation_count;
120     HANDLE event;
121
122     EnterCriticalSection (&cond->lock);
123     /* Increment waiters_count,
124        and get a copy of the current wait_generation_count.  */
125     cond->waiters_count++;
126     old_generation_count = cond->wait_generation_count;
127     event = cond->event;
128     LeaveCriticalSection (&cond->lock);
129
130     {
131       /* Now release the lock and let any other thread take it.  */
132       int err = glthread_lock_unlock (lock);
133       if (err != 0)
134         {
135           EnterCriticalSection (&cond->lock);
136           cond->waiters_count--;
137           LeaveCriticalSection (&cond->lock);
138           return err;
139         }
140
141       {
142         /* Wait until another thread signals this event.  */
143         DWORD result;
144
145         for (;;)
146           {
147             bool wait_done;
148
149             result = WaitForSingleObject (event, INFINITE);
150             if (result != WAIT_OBJECT_0)
151               break;
152             /* Distingish intended from spurious wakeups.  */
153             EnterCriticalSection (&cond->lock);
154             wait_done =
155               (cond->release_count > 0
156                && cond->wait_generation_count != old_generation_count);
157             LeaveCriticalSection (&cond->lock);
158             if (wait_done)
159               break;
160           }
161
162         /* Take the lock again.  */
163         err = glthread_lock_lock (lock);
164
165         /* Do the bookkeeping.  */
166         EnterCriticalSection (&cond->lock);
167         cond->waiters_count--;
168         if (result == WAIT_OBJECT_0)
169           {
170             /* The wait terminated because the event was signaled.
171                Acknowledge the receipt.  */
172             if (--cond->release_count == 0)
173               {
174                 /* The last waiting thread to be notified has to reset
175                    the event.  */
176                 ResetEvent (cond->event);
177               }
178           }
179         LeaveCriticalSection (&cond->lock);
180
181         return (err ? err :
182                 result == WAIT_OBJECT_0 ? 0 :
183                 /* WAIT_FAILED shouldn't happen */ EAGAIN);
184       }
185     }
186   }
187 }
188
189 int
190 glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
191 {
192   struct timeval currtime;
193
194   gettimeofday (&currtime, NULL);
195   if (currtime.tv_sec > abstime->tv_sec
196       || (currtime.tv_sec == abstime->tv_sec
197           && currtime.tv_usec * 1000 >= abstime->tv_nsec))
198     return ETIMEDOUT;
199
200   if (!cond->guard.done)
201     {
202       if (InterlockedIncrement (&cond->guard.started) == 0)
203         /* This thread is the first one to need this condition variable.
204            Initialize it.  */
205         glthread_cond_init (cond);
206       else
207         /* Yield the CPU while waiting for another thread to finish
208            initializing this condition variable.  */
209         while (!cond->guard.done)
210           Sleep (0);
211     }
212
213   {
214     unsigned old_generation_count;
215     HANDLE event;
216
217     EnterCriticalSection (&cond->lock);
218     /* Increment waiters_count,
219        and get a copy of the current wait_generation_count.  */
220     cond->waiters_count++;
221     old_generation_count = cond->wait_generation_count;
222     event = cond->event;
223     LeaveCriticalSection (&cond->lock);
224
225     {
226       /* Now release the lock and let any other thread take it.  */
227       int err = glthread_lock_unlock (lock);
228       if (err != 0)
229         {
230           EnterCriticalSection (&cond->lock);
231           cond->waiters_count--;
232           LeaveCriticalSection (&cond->lock);
233           return err;
234         }
235
236       {
237         /* Wait until another thread signals this event or until the abstime
238            passes.  */
239         DWORD result;
240
241         for (;;)
242           {
243             DWORD timeout;
244             bool wait_done;
245
246             gettimeofday (&currtime, NULL);
247             if (currtime.tv_sec > abstime->tv_sec)
248               timeout = 0;
249             else
250               {
251                 unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
252                 timeout = seconds * 1000;
253                 if (timeout / 1000 != seconds) /* overflow? */
254                   timeout = INFINITE;
255                 else
256                   {
257                     long milliseconds =
258                       abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
259                     if (milliseconds >= 0)
260                       {
261                         timeout += milliseconds;
262                         if (timeout < milliseconds) /* overflow? */
263                           timeout = INFINITE;
264                       }
265                     else
266                       {
267                         if (timeout >= - milliseconds)
268                           timeout -= (- milliseconds);
269                         else
270                           timeout = 0;
271                       }
272                   }
273               }
274
275             result = WaitForSingleObject (event, timeout);
276             if (result != WAIT_OBJECT_0)
277               break;
278             /* Distingish intended from spurious wakeups.  */
279             EnterCriticalSection (&cond->lock);
280             wait_done =
281               (cond->release_count > 0
282                && cond->wait_generation_count != old_generation_count);
283             LeaveCriticalSection (&cond->lock);
284             if (wait_done)
285               break;
286           }
287
288         /* Take the lock again.  */
289         err = glthread_lock_lock (lock);
290
291         /* Do the bookkeeping.  */
292         EnterCriticalSection (&cond->lock);
293         cond->waiters_count--;
294         if (result == WAIT_OBJECT_0)
295           {
296             /* The wait terminated because the event was signaled.
297                Acknowledge the receipt.  */
298             if (--cond->release_count == 0)
299               {
300                 /* The last waiting thread to be notified has to reset
301                    the event.  */
302                 ResetEvent (cond->event);
303               }
304           }
305         LeaveCriticalSection (&cond->lock);
306
307         return (err ? err :
308                 result == WAIT_OBJECT_0 ? 0 :
309                 result == WAIT_TIMEOUT ? ETIMEDOUT :
310                 /* WAIT_FAILED shouldn't happen */ EAGAIN);
311       }
312     }
313   }
314 }
315
316 int
317 glthread_cond_signal_func (gl_cond_t *cond)
318 {
319   if (!cond->guard.done)
320     return EINVAL;
321
322   EnterCriticalSection (&cond->lock);
323   /* POSIX says:
324       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
325        have no effect if there are no threads currently blocked on cond."
326      Also, if  waiters_count == release_count > 0, all blocked threads will
327      be unblocked soon anyway; do nothing in this case as well.  */
328   if (cond->waiters_count > cond->release_count)
329     {
330       /* Signal the manual-reset event.  */
331       SetEvent (cond->event);
332       /* ... and reset it is soon as one of the currently waiting threads has
333          acknowledged the receipt of the signal.  */
334       cond->release_count++;
335       cond->wait_generation_count++;
336     }
337   LeaveCriticalSection (&cond->lock);
338
339   return 0;
340 }
341
342 int
343 glthread_cond_broadcast_func (gl_cond_t *cond)
344 {
345   if (!cond->guard.done)
346     return EINVAL;
347
348   EnterCriticalSection (&cond->lock);
349   /* POSIX says:
350       "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
351        have no effect if there are no threads currently blocked on cond."  */
352   if (cond->waiters_count > 0)
353     {
354       /* Signal the manual-reset event.  */
355       SetEvent (cond->event);
356       /* ... and reset it only after all currently waiting threads have
357          acknowledged the receipt of the signal.  */
358       cond->release_count = cond->waiters_count;
359       cond->wait_generation_count++;
360     }
361   LeaveCriticalSection (&cond->lock);
362
363   return 0;
364 }
365
366 int
367 glthread_cond_destroy_func (gl_cond_t *cond)
368 {
369   if (!cond->guard.done)
370     return EINVAL;
371   if (cond->waiters_count > 0)
372     return EBUSY;
373   CloseHandle (cond->event);
374   DeleteCriticalSection (&cond->lock);
375   cond->guard.done = 0;
376   return 0;
377 }
378
379 #endif
380
381 /* ========================================================================= */