GCond: use monotonic time for timed waits
[platform/upstream/glib.git] / glib / gthread-win32.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * gthread.c: solaris thread system implementation
5  * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe
6  * Copyright 2001 Hans Breuer
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
26  * file for a list of people on the GLib Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GLib at ftp://ftp.gtk.org/pub/gtk/.
29  */
30
31 /* The GMutex and GCond implementations in this file are some of the
32  * lowest-level code in GLib.  All other parts of GLib (messages,
33  * memory, slices, etc) assume that they can freely use these facilities
34  * without risking recursion.
35  *
36  * As such, these functions are NOT permitted to call any other part of
37  * GLib.
38  *
39  * The thread manipulation functions (create, exit, join, etc.) have
40  * more freedom -- they can do as they please.
41  */
42
43 #include "config.h"
44
45 #include "gthread.h"
46 #include "gthreadprivate.h"
47 #include "gslice.h"
48
49 #include <windows.h>
50
51 #include <process.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54
55 static void
56 g_thread_abort (gint         status,
57                 const gchar *function)
58 {
59   fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s.  Aborting.\n",
60            strerror (status), function);
61   abort ();
62 }
63
64 /* Starting with Vista and Windows 2008, we have access to the
65  * CONDITION_VARIABLE and SRWLock primatives on Windows, which are
66  * pretty reasonable approximations of the primatives specified in
67  * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively).
68  *
69  * Both of these types are structs containing a single pointer.  That
70  * pointer is used as an atomic bitfield to support user-space mutexes
71  * that only get the kernel involved in cases of contention (similar
72  * to how futex()-based mutexes work on Linux).  The biggest advantage
73  * of these new types is that they can be statically initialised to
74  * zero.  That means that they are completely ABI compatible with our
75  * GMutex and GCond APIs.
76  *
77  * Unfortunately, Windows XP lacks these facilities and GLib still
78  * needs to support Windows XP.  Our approach here is as follows:
79  *
80  *   - avoid depending on structure declarations at compile-time by
81  *     declaring our own GMutex and GCond strutures to be
82  *     ABI-compatible with SRWLock and CONDITION_VARIABLE and using
83  *     those instead
84  *
85  *   - avoid a hard dependency on the symbols used to manipulate these
86  *     structures by doing a dynamic lookup of those symbols at
87  *     runtime
88  *
89  *   - if the symbols are not available, emulate them using other
90  *     primatives
91  *
92  * Using this approach also allows us to easily build a GLib that lacks
93  * support for Windows XP or to remove this code entirely when XP is no
94  * longer supported (end of line is currently April 8, 2014).
95  */
96 typedef struct
97 {
98   void     (__stdcall * CallThisOnThreadExit)        (void);              /* fake */
99
100   void     (__stdcall * InitializeSRWLock)           (gpointer lock);
101   void     (__stdcall * DeleteSRWLock)               (gpointer lock);     /* fake */
102   void     (__stdcall * AcquireSRWLockExclusive)     (gpointer lock);
103   BOOLEAN  (__stdcall * TryAcquireSRWLockExclusive)  (gpointer lock);
104   void     (__stdcall * ReleaseSRWLockExclusive)     (gpointer lock);
105   void     (__stdcall * AcquireSRWLockShared)        (gpointer lock);
106   BOOLEAN  (__stdcall * TryAcquireSRWLockShared)     (gpointer lock);
107   void     (__stdcall * ReleaseSRWLockShared)        (gpointer lock);
108
109   void     (__stdcall * InitializeConditionVariable) (gpointer cond);
110   void     (__stdcall * DeleteConditionVariable)     (gpointer cond);     /* fake */
111   BOOL     (__stdcall * SleepConditionVariableSRW)   (gpointer cond,
112                                                       gpointer lock,
113                                                       DWORD    timeout,
114                                                       ULONG    flags);
115   void     (__stdcall * WakeAllConditionVariable)    (gpointer cond);
116   void     (__stdcall * WakeConditionVariable)       (gpointer cond);
117 } GThreadImplVtable;
118
119 static GThreadImplVtable g_thread_impl_vtable;
120
121 /* {{{1 GMutex */
122 void
123 g_mutex_init (GMutex *mutex)
124 {
125   g_thread_impl_vtable.InitializeSRWLock (mutex);
126 }
127
128 void
129 g_mutex_clear (GMutex *mutex)
130 {
131   if (g_thread_impl_vtable.DeleteSRWLock != NULL)
132     g_thread_impl_vtable.DeleteSRWLock (mutex);
133 }
134
135 void
136 g_mutex_lock (GMutex *mutex)
137 {
138   g_thread_impl_vtable.AcquireSRWLockExclusive (mutex);
139 }
140
141 gboolean
142 g_mutex_trylock (GMutex *mutex)
143 {
144   return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex);
145 }
146
147 void
148 g_mutex_unlock (GMutex *mutex)
149 {
150   g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex);
151 }
152
153 /* {{{1 GRecMutex */
154
155 static CRITICAL_SECTION *
156 g_rec_mutex_impl_new (void)
157 {
158   CRITICAL_SECTION *cs;
159
160   cs = g_slice_new (CRITICAL_SECTION);
161   InitializeCriticalSection (cs);
162
163   return cs;
164 }
165
166 static void
167 g_rec_mutex_impl_free (CRITICAL_SECTION *cs)
168 {
169   DeleteCriticalSection (cs);
170   g_slice_free (CRITICAL_SECTION, cs);
171 }
172
173 static CRITICAL_SECTION *
174 g_rec_mutex_get_impl (GRecMutex *mutex)
175 {
176   CRITICAL_SECTION *impl = mutex->p;
177
178   if G_UNLIKELY (mutex->p == NULL)
179     {
180       impl = g_rec_mutex_impl_new ();
181       if (InterlockedCompareExchangePointer (&mutex->p, impl, NULL) != NULL)
182         g_rec_mutex_impl_free (impl);
183       impl = mutex->p;
184     }
185
186   return impl;
187 }
188
189 void
190 g_rec_mutex_init (GRecMutex *mutex)
191 {
192   mutex->p = g_rec_mutex_impl_new ();
193 }
194
195 void
196 g_rec_mutex_clear (GRecMutex *mutex)
197 {
198   g_rec_mutex_impl_free (mutex->p);
199 }
200
201 void
202 g_rec_mutex_lock (GRecMutex *mutex)
203 {
204   EnterCriticalSection (g_rec_mutex_get_impl (mutex));
205 }
206
207 void
208 g_rec_mutex_unlock (GRecMutex *mutex)
209 {
210   LeaveCriticalSection (mutex->p);
211 }
212
213 gboolean
214 g_rec_mutex_trylock (GRecMutex *mutex)
215 {
216   return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex));
217 }
218
219 /* {{{1 GRWLock */
220
221 void
222 g_rw_lock_init (GRWLock *lock)
223 {
224   g_thread_impl_vtable.InitializeSRWLock (lock);
225 }
226
227 void
228 g_rw_lock_clear (GRWLock *lock)
229 {
230   if (g_thread_impl_vtable.DeleteSRWLock != NULL)
231     g_thread_impl_vtable.DeleteSRWLock (lock);
232 }
233
234 void
235 g_rw_lock_writer_lock (GRWLock *lock)
236 {
237   g_thread_impl_vtable.AcquireSRWLockExclusive (lock);
238 }
239
240 gboolean
241 g_rw_lock_writer_trylock (GRWLock *lock)
242 {
243   return g_thread_impl_vtable.TryAcquireSRWLockExclusive (lock);
244 }
245
246 void
247 g_rw_lock_writer_unlock (GRWLock *lock)
248 {
249   g_thread_impl_vtable.ReleaseSRWLockExclusive (lock);
250 }
251
252 void
253 g_rw_lock_reader_lock (GRWLock *lock)
254 {
255   g_thread_impl_vtable.AcquireSRWLockShared (lock);
256 }
257
258 gboolean
259 g_rw_lock_reader_trylock (GRWLock *lock)
260 {
261   return g_thread_impl_vtable.TryAcquireSRWLockShared (lock);
262 }
263
264 void
265 g_rw_lock_reader_unlock (GRWLock *lock)
266 {
267   g_thread_impl_vtable.ReleaseSRWLockShared (lock);
268 }
269
270 /* {{{1 GCond */
271 void
272 g_cond_init (GCond *cond)
273 {
274   g_thread_impl_vtable.InitializeConditionVariable (cond);
275 }
276
277 void
278 g_cond_clear (GCond *cond)
279 {
280   if (g_thread_impl_vtable.DeleteConditionVariable)
281     g_thread_impl_vtable.DeleteConditionVariable (cond);
282 }
283
284 void
285 g_cond_signal (GCond *cond)
286 {
287   g_thread_impl_vtable.WakeConditionVariable (cond);
288 }
289
290 void
291 g_cond_broadcast (GCond *cond)
292 {
293   g_thread_impl_vtable.WakeAllConditionVariable (cond);
294 }
295
296 void
297 g_cond_wait (GCond  *cond,
298              GMutex *entered_mutex)
299 {
300   g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0);
301 }
302
303 gboolean
304 g_cond_wait_until (GCond  *cond,
305                    GMutex *entered_mutex,
306                    gint64  end_time)
307 {
308   gint64 span;
309   FILETIME ft;
310   gint64 now;
311
312   GetSystemTimeAsFileTime (&ft);
313   memmove (&now, &ft, sizeof (FILETIME));
314
315   now -= G_GINT64_CONSTANT (116444736000000000);
316   now /= 10;
317
318   span = end_time - now;
319
320   if G_UNLIKELY (span < 0)
321     span = 0;
322
323   if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32)
324     span = INFINITE;
325
326   return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0);
327 }
328
329 /* {{{1 GPrivate */
330
331 typedef struct _GPrivateDestructor GPrivateDestructor;
332
333 struct _GPrivateDestructor
334 {
335   DWORD               index;
336   GDestroyNotify      notify;
337   GPrivateDestructor *next;
338 };
339
340 static GPrivateDestructor * volatile g_private_destructors;
341 static CRITICAL_SECTION g_private_lock;
342
343 static DWORD
344 g_private_get_impl (GPrivate *key)
345 {
346   DWORD impl = (DWORD) key->p;
347
348   if G_UNLIKELY (impl == 0)
349     {
350       EnterCriticalSection (&g_private_lock);
351       impl = (DWORD) key->p;
352       if (impl == 0)
353         {
354           GPrivateDestructor *destructor;
355
356           impl = TlsAlloc ();
357
358           if (impl == TLS_OUT_OF_INDEXES)
359             g_thread_abort (0, "TlsAlloc");
360
361           if (key->notify != NULL)
362             {
363               destructor = malloc (sizeof (GPrivateDestructor));
364               if G_UNLIKELY (destructor == NULL)
365                 g_thread_abort (errno, "malloc");
366               destructor->index = impl;
367               destructor->notify = key->notify;
368               destructor->next = g_private_destructors;
369
370               /* We need to do an atomic store due to the unlocked
371                * access to the destructor list from the thread exit
372                * function.
373                *
374                * It can double as a sanity check...
375                */
376               if (InterlockedCompareExchangePointer (&g_private_destructors, destructor,
377                                                      destructor->next) != destructor->next)
378                 g_thread_abort (0, "g_private_get_impl(1)");
379             }
380
381           /* Ditto, due to the unlocked access on the fast path */
382           if (InterlockedCompareExchangePointer (&key->p, impl, NULL) != NULL)
383             g_thread_abort (0, "g_private_get_impl(2)");
384         }
385       LeaveCriticalSection (&g_private_lock);
386     }
387
388   return impl;
389 }
390
391 gpointer
392 g_private_get (GPrivate *key)
393 {
394   return TlsGetValue (g_private_get_impl (key));
395 }
396
397 void
398 g_private_set (GPrivate *key,
399                gpointer  value)
400 {
401   TlsSetValue (g_private_get_impl (key), value);
402 }
403
404 void
405 g_private_replace (GPrivate *key,
406                    gpointer  value)
407 {
408   DWORD impl = g_private_get_impl (key);
409   gpointer old;
410
411   old = TlsGetValue (impl);
412   if (old && key->notify)
413     key->notify (old);
414   TlsSetValue (impl, value);
415 }
416
417 /* {{{1 GThread */
418
419 #include "glib.h"
420 #include "gthreadprivate.h"
421
422 #define win32_check_for_error(what) G_STMT_START{                       \
423   if (!(what))                                                          \
424     g_error ("file %s: line %d (%s): error %s during %s",               \
425              __FILE__, __LINE__, G_STRFUNC,                             \
426              g_win32_error_message (GetLastError ()), #what);           \
427   }G_STMT_END
428
429 #define G_MUTEX_SIZE (sizeof (gpointer))
430
431 typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
432
433 typedef struct
434 {
435   GRealThread thread;
436
437   GThreadFunc proxy;
438   HANDLE      handle;
439 } GThreadWin32;
440
441 void
442 g_system_thread_free (GRealThread *thread)
443 {
444   GThreadWin32 *wt = (GThreadWin32 *) thread;
445
446   win32_check_for_error (CloseHandle (wt->handle));
447   g_slice_free (GThreadWin32, wt);
448 }
449
450 void
451 g_system_thread_exit (void)
452 {
453   _endthreadex (0);
454 }
455
456 static guint __stdcall
457 g_thread_win32_proxy (gpointer data)
458 {
459   GThreadWin32 *self = data;
460
461   self->proxy (self);
462
463   g_system_thread_exit ();
464
465   g_assert_not_reached ();
466
467   return 0;
468 }
469
470 GRealThread *
471 g_system_thread_new (GThreadFunc   func,
472                      gulong        stack_size,
473                      GError      **error)
474 {
475   GThreadWin32 *thread;
476   guint ignore;
477
478   thread = g_slice_new0 (GThreadWin32);
479   thread->proxy = func;
480
481   thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread, 0, &ignore);
482
483   if (thread->handle == NULL)
484     {
485       gchar *win_error = g_win32_error_message (GetLastError ());
486       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
487                    "Error creating thread: %s", win_error);
488       g_free (win_error);
489       g_slice_free (GThreadWin32, thread);
490       return NULL;
491     }
492
493   return (GRealThread *) thread;
494 }
495
496 void
497 g_thread_yield (void)
498 {
499   Sleep(0);
500 }
501
502 void
503 g_system_thread_wait (GRealThread *thread)
504 {
505   GThreadWin32 *wt = (GThreadWin32 *) thread;
506
507   win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE));
508 }
509
510 void
511 g_system_thread_set_name (const gchar *name)
512 {
513   /* FIXME: implement */
514 }
515
516 /* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
517
518 static CRITICAL_SECTION g_thread_xp_lock;
519 static DWORD            g_thread_xp_waiter_tls;
520
521 /* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
522 typedef struct _GThreadXpWaiter GThreadXpWaiter;
523 struct _GThreadXpWaiter
524 {
525   HANDLE                    event;
526   volatile GThreadXpWaiter *next;
527 };
528
529 static GThreadXpWaiter *
530 g_thread_xp_waiter_get (void)
531 {
532   GThreadXpWaiter *waiter;
533
534   waiter = TlsGetValue (g_thread_xp_waiter_tls);
535
536   if G_UNLIKELY (waiter == NULL)
537     {
538       waiter = malloc (sizeof (GThreadXpWaiter));
539       if (waiter == NULL)
540         g_thread_abort (GetLastError (), "malloc");
541       waiter->event = CreateEvent (0, FALSE, FALSE, NULL);
542       if (waiter->event == NULL)
543         g_thread_abort (GetLastError (), "CreateEvent");
544
545       TlsSetValue (g_thread_xp_waiter_tls, waiter);
546     }
547
548   return waiter;
549 }
550
551 static void __stdcall
552 g_thread_xp_CallThisOnThreadExit (void)
553 {
554   GThreadXpWaiter *waiter;
555
556   waiter = TlsGetValue (g_thread_xp_waiter_tls);
557
558   if (waiter != NULL)
559     {
560       TlsSetValue (g_thread_xp_waiter_tls, NULL);
561       CloseHandle (waiter->event);
562       free (waiter);
563     }
564 }
565
566 /* {{{2 SRWLock emulation */
567 typedef struct
568 {
569   CRITICAL_SECTION  writer_lock;
570   gboolean          ever_shared;    /* protected by writer_lock */
571   gboolean          writer_locked;  /* protected by writer_lock */
572
573   /* below is only ever touched if ever_shared becomes true */
574   CRITICAL_SECTION  atomicity;
575   GThreadXpWaiter  *queued_writer; /* protected by atomicity lock */
576   gint              num_readers;   /* protected by atomicity lock */
577 } GThreadSRWLock;
578
579 static void __stdcall
580 g_thread_xp_InitializeSRWLock (gpointer mutex)
581 {
582   *(GThreadSRWLock * volatile *) mutex = NULL;
583 }
584
585 static void __stdcall
586 g_thread_xp_DeleteSRWLock (gpointer mutex)
587 {
588   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
589
590   if (lock)
591     {
592       if (lock->ever_shared)
593         DeleteCriticalSection (&lock->atomicity);
594
595       DeleteCriticalSection (&lock->writer_lock);
596       free (lock);
597     }
598 }
599
600 static GThreadSRWLock * __stdcall
601 g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
602 {
603   GThreadSRWLock *result;
604
605   /* It looks like we're missing some barriers here, but this code only
606    * ever runs on Windows XP, which in turn only ever runs on hardware
607    * with a relatively rigid memory model.  The 'volatile' will take
608    * care of the compiler.
609    */
610   result = *lock;
611
612   if G_UNLIKELY (result == NULL)
613     {
614       EnterCriticalSection (&g_thread_xp_lock);
615
616       result = malloc (sizeof (GThreadSRWLock));
617
618       if (result == NULL)
619         g_thread_abort (errno, "malloc");
620
621       InitializeCriticalSection (&result->writer_lock);
622       result->writer_locked = FALSE;
623       result->ever_shared = FALSE;
624       *lock = result;
625
626       LeaveCriticalSection (&g_thread_xp_lock);
627     }
628
629   return result;
630 }
631
632 static void __stdcall
633 g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
634 {
635   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
636
637   EnterCriticalSection (&lock->writer_lock);
638
639   /* CRITICAL_SECTION is reentrant, but SRWLock is not.
640    * Detect the deadlock that would occur on later Windows version.
641    */
642   g_assert (!lock->writer_locked);
643   lock->writer_locked = TRUE;
644
645   if (lock->ever_shared)
646     {
647       GThreadXpWaiter *waiter = NULL;
648
649       EnterCriticalSection (&lock->atomicity);
650       if (lock->num_readers > 0)
651         lock->queued_writer = waiter = g_thread_xp_waiter_get ();
652       LeaveCriticalSection (&lock->atomicity);
653
654       if (waiter != NULL)
655         WaitForSingleObject (waiter->event, INFINITE);
656
657       lock->queued_writer = NULL;
658     }
659 }
660
661 static BOOLEAN __stdcall
662 g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
663 {
664   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
665
666   if (!TryEnterCriticalSection (&lock->writer_lock))
667     return FALSE;
668
669   /* CRITICAL_SECTION is reentrant, but SRWLock is not.
670    * Ensure that this properly returns FALSE (as SRWLock would).
671    */
672   if G_UNLIKELY (lock->writer_locked)
673     {
674       LeaveCriticalSection (&lock->writer_lock);
675       return FALSE;
676     }
677
678   lock->writer_locked = TRUE;
679
680   if (lock->ever_shared)
681     {
682       gboolean available;
683
684       EnterCriticalSection (&lock->atomicity);
685       available = lock->num_readers == 0;
686       LeaveCriticalSection (&lock->atomicity);
687
688       if (!available)
689         {
690           LeaveCriticalSection (&lock->writer_lock);
691           return FALSE;
692         }
693     }
694
695   return TRUE;
696 }
697
698 static void __stdcall
699 g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
700 {
701   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
702
703   lock->writer_locked = FALSE;
704
705   /* We need this until we fix some weird parts of GLib that try to
706    * unlock freshly-allocated mutexes.
707    */
708   if (lock != NULL)
709     LeaveCriticalSection (&lock->writer_lock);
710 }
711
712 static void
713 g_thread_xp_srwlock_become_reader (GThreadSRWLock *lock)
714 {
715   if G_UNLIKELY (!lock->ever_shared)
716     {
717       InitializeCriticalSection (&lock->atomicity);
718       lock->queued_writer = NULL;
719       lock->num_readers = 0;
720
721       lock->ever_shared = TRUE;
722     }
723
724   EnterCriticalSection (&lock->atomicity);
725   lock->num_readers++;
726   LeaveCriticalSection (&lock->atomicity);
727 }
728
729 static void __stdcall
730 g_thread_xp_AcquireSRWLockShared (gpointer mutex)
731 {
732   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
733
734   EnterCriticalSection (&lock->writer_lock);
735
736   /* See g_thread_xp_AcquireSRWLockExclusive */
737   g_assert (!lock->writer_locked);
738
739   g_thread_xp_srwlock_become_reader (lock);
740
741   LeaveCriticalSection (&lock->writer_lock);
742 }
743
744 static BOOLEAN __stdcall
745 g_thread_xp_TryAcquireSRWLockShared (gpointer mutex)
746 {
747   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
748
749   if (!TryEnterCriticalSection (&lock->writer_lock))
750     return FALSE;
751
752   /* See g_thread_xp_AcquireSRWLockExclusive */
753   if G_UNLIKELY (lock->writer_locked)
754     {
755       LeaveCriticalSection (&lock->writer_lock);
756       return FALSE;
757     }
758
759   g_thread_xp_srwlock_become_reader (lock);
760
761   LeaveCriticalSection (&lock->writer_lock);
762
763   return TRUE;
764 }
765
766 static void __stdcall
767 g_thread_xp_ReleaseSRWLockShared (gpointer mutex)
768 {
769   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
770
771   EnterCriticalSection (&lock->atomicity);
772
773   lock->num_readers--;
774
775   if (lock->num_readers == 0 && lock->queued_writer)
776     SetEvent (lock->queued_writer->event);
777
778   LeaveCriticalSection (&lock->atomicity);
779 }
780
781 /* {{{2 CONDITION_VARIABLE emulation */
782 typedef struct
783 {
784   volatile GThreadXpWaiter  *first;
785   volatile GThreadXpWaiter **last_ptr;
786 } GThreadXpCONDITION_VARIABLE;
787
788 static void __stdcall
789 g_thread_xp_InitializeConditionVariable (gpointer cond)
790 {
791   *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL;
792 }
793
794 static void __stdcall
795 g_thread_xp_DeleteConditionVariable (gpointer cond)
796 {
797   GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond;
798
799   if (cv)
800     free (cv);
801 }
802
803 static GThreadXpCONDITION_VARIABLE * __stdcall
804 g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond)
805 {
806   GThreadXpCONDITION_VARIABLE *result;
807
808   /* It looks like we're missing some barriers here, but this code only
809    * ever runs on Windows XP, which in turn only ever runs on hardware
810    * with a relatively rigid memory model.  The 'volatile' will take
811    * care of the compiler.
812    */
813   result = *cond;
814
815   if G_UNLIKELY (result == NULL)
816     {
817       result = malloc (sizeof (GThreadXpCONDITION_VARIABLE));
818
819       if (result == NULL)
820         g_thread_abort (errno, "malloc");
821
822       result->first = NULL;
823       result->last_ptr = &result->first;
824
825       if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL)
826         {
827           free (result);
828           result = *cond;
829         }
830     }
831
832   return result;
833 }
834
835 static BOOL __stdcall
836 g_thread_xp_SleepConditionVariableSRW (gpointer cond,
837                                        gpointer mutex,
838                                        DWORD    timeout,
839                                        ULONG    flags)
840 {
841   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
842   GThreadXpWaiter *waiter = g_thread_xp_waiter_get ();
843   DWORD status;
844
845   waiter->next = NULL;
846
847   EnterCriticalSection (&g_thread_xp_lock);
848   *cv->last_ptr = waiter;
849   cv->last_ptr = &waiter->next;
850   LeaveCriticalSection (&g_thread_xp_lock);
851
852   g_mutex_unlock (mutex);
853   status = WaitForSingleObject (waiter->event, timeout);
854
855   if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0)
856     g_thread_abort (GetLastError (), "WaitForSingleObject");
857
858   g_mutex_lock (mutex);
859
860   return status == WAIT_OBJECT_0;
861 }
862
863 static void __stdcall
864 g_thread_xp_WakeConditionVariable (gpointer cond)
865 {
866   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
867   volatile GThreadXpWaiter *waiter;
868
869   EnterCriticalSection (&g_thread_xp_lock);
870   waiter = cv->first;
871   if (waiter != NULL)
872     {
873       cv->first = waiter->next;
874       if (cv->first == NULL)
875         cv->last_ptr = &cv->first;
876     }
877   LeaveCriticalSection (&g_thread_xp_lock);
878
879   if (waiter != NULL)
880     SetEvent (waiter->event);
881 }
882
883 static void __stdcall
884 g_thread_xp_WakeAllConditionVariable (gpointer cond)
885 {
886   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
887   volatile GThreadXpWaiter *waiter;
888
889   EnterCriticalSection (&g_thread_xp_lock);
890   waiter = cv->first;
891   cv->first = NULL;
892   cv->last_ptr = &cv->first;
893   LeaveCriticalSection (&g_thread_xp_lock);
894
895   while (waiter != NULL)
896     {
897       volatile GThreadXpWaiter *next;
898
899       next = waiter->next;
900       SetEvent (waiter->event);
901       waiter = next;
902     }
903 }
904
905 /* {{{2 XP Setup */
906 static void
907 g_thread_xp_init (void)
908 {
909   static const GThreadImplVtable g_thread_xp_impl_vtable = {
910     g_thread_xp_CallThisOnThreadExit,
911     g_thread_xp_InitializeSRWLock,
912     g_thread_xp_DeleteSRWLock,
913     g_thread_xp_AcquireSRWLockExclusive,
914     g_thread_xp_TryAcquireSRWLockExclusive,
915     g_thread_xp_ReleaseSRWLockExclusive,
916     g_thread_xp_AcquireSRWLockShared,
917     g_thread_xp_TryAcquireSRWLockShared,
918     g_thread_xp_ReleaseSRWLockShared,
919     g_thread_xp_InitializeConditionVariable,
920     g_thread_xp_DeleteConditionVariable,
921     g_thread_xp_SleepConditionVariableSRW,
922     g_thread_xp_WakeAllConditionVariable,
923     g_thread_xp_WakeConditionVariable
924   };
925
926   InitializeCriticalSection (&g_thread_xp_lock);
927   g_thread_xp_waiter_tls = TlsAlloc ();
928
929   g_thread_impl_vtable = g_thread_xp_impl_vtable;
930 }
931
932 /* {{{1 Epilogue */
933
934 static gboolean
935 g_thread_lookup_native_funcs (void)
936 {
937   GThreadImplVtable native_vtable = { 0, };
938   HMODULE kernel32;
939
940   kernel32 = GetModuleHandle ("KERNEL32.DLL");
941
942   if (kernel32 == NULL)
943     return FALSE;
944
945 #define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE
946   GET_FUNC(InitializeSRWLock);
947   GET_FUNC(AcquireSRWLockExclusive);
948   GET_FUNC(TryAcquireSRWLockExclusive);
949   GET_FUNC(ReleaseSRWLockExclusive);
950   GET_FUNC(AcquireSRWLockShared);
951   GET_FUNC(TryAcquireSRWLockShared);
952   GET_FUNC(ReleaseSRWLockShared);
953
954   GET_FUNC(InitializeConditionVariable);
955   GET_FUNC(SleepConditionVariableSRW);
956   GET_FUNC(WakeAllConditionVariable);
957   GET_FUNC(WakeConditionVariable);
958 #undef GET_FUNC
959
960   g_thread_impl_vtable = native_vtable;
961
962   return TRUE;
963 }
964
965 G_GNUC_INTERNAL void
966 g_thread_win32_init (void)
967 {
968   if (g_thread_lookup_native_funcs ())
969     fprintf (stderr, "(debug) GThread using native mode\n");
970   else
971     {
972       fprintf (stderr, "(debug) GThread using Windows XP mode\n");
973       g_thread_xp_init ();
974     }
975
976   InitializeCriticalSection (&g_private_lock);
977 }
978
979 G_GNUC_INTERNAL void
980 g_thread_win32_thread_detach (void)
981 {
982   gboolean dtors_called;
983
984   do
985     {
986       GPrivateDestructor *dtor;
987
988       /* We go by the POSIX book on this one.
989        *
990        * If we call a destructor then there is a chance that some new
991        * TLS variables got set by code called in that destructor.
992        *
993        * Loop until nothing is left.
994        */
995       dtors_called = FALSE;
996
997       for (dtor = g_private_destructors; dtor; dtor = dtor->next)
998         {
999           gpointer value;
1000
1001           value = TlsGetValue (dtor->index);
1002           if (value != NULL && dtor->notify != NULL)
1003             {
1004               /* POSIX says to clear this before the call */
1005               TlsSetValue (dtor->index, NULL);
1006               dtor->notify (value);
1007               dtors_called = TRUE;
1008             }
1009         }
1010     }
1011   while (dtors_called);
1012
1013   if (g_thread_impl_vtable.CallThisOnThreadExit)
1014     g_thread_impl_vtable.CallThisOnThreadExit ();
1015 }
1016
1017 /* vim:set foldmethod=marker: */