locks: drop _INIT macros
[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->impl;
177
178   if G_UNLIKELY (mutex->impl == NULL)
179     {
180       impl = g_rec_mutex_impl_new ();
181       if (InterlockedCompareExchangePointer (&mutex->impl, impl, NULL) != NULL)
182         g_rec_mutex_impl_free (impl);
183       impl = mutex->impl;
184     }
185
186   return impl;
187 }
188
189 void
190 g_rec_mutex_init (GRecMutex *mutex)
191 {
192   mutex->impl = g_rec_mutex_impl_new ();
193 }
194
195 void
196 g_rec_mutex_clear (GRecMutex *mutex)
197 {
198   if (mutex->impl)
199     g_rec_mutex_impl_free (mutex->impl);
200 }
201
202 void
203 g_rec_mutex_lock (GRecMutex *mutex)
204 {
205   EnterCriticalSection (g_rec_mutex_get_impl (mutex));
206 }
207
208 void
209 g_rec_mutex_unlock (GRecMutex *mutex)
210 {
211   LeaveCriticalSection (mutex->impl);
212 }
213
214 gboolean
215 g_rec_mutex_trylock (GRecMutex *mutex)
216 {
217   return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex));
218 }
219
220 /* {{{1 GRWLock */
221
222 void
223 g_rw_lock_init (GRWLock *lock)
224 {
225   g_thread_impl_vtable.InitializeSRWLock (lock);
226 }
227
228 void
229 g_rw_lock_clear (GRWLock *lock)
230 {
231   if (g_thread_impl_vtable.DeleteSRWLock != NULL)
232     g_thread_impl_vtable.DeleteSRWLock (lock);
233 }
234
235 void
236 g_rw_lock_writer_lock (GRWLock *lock)
237 {
238   g_thread_impl_vtable.AcquireSRWLockExclusive (lock);
239 }
240
241 gboolean
242 g_rw_lock_writer_trylock (GRWLock *lock)
243 {
244   return g_thread_impl_vtable.TryAcquireSRWLockExclusive (lock);
245 }
246
247 void
248 g_rw_lock_writer_unlock (GRWLock *lock)
249 {
250   g_thread_impl_vtable.ReleaseSRWLockExclusive (lock);
251 }
252
253 void
254 g_rw_lock_reader_lock (GRWLock *lock)
255 {
256   g_thread_impl_vtable.AcquireSRWLockShared (lock);
257 }
258
259 gboolean
260 g_rw_lock_reader_trylock (GRWLock *lock)
261 {
262   return g_thread_impl_vtable.TryAcquireSRWLockShared (lock);
263 }
264
265 void
266 g_rw_lock_reader_unlock (GRWLock *lock)
267 {
268   g_thread_impl_vtable.ReleaseSRWLockShared (lock);
269 }
270
271 /* {{{1 GCond */
272 void
273 g_cond_init (GCond *cond)
274 {
275   g_thread_impl_vtable.InitializeConditionVariable (cond);
276 }
277
278 void
279 g_cond_clear (GCond *cond)
280 {
281   if (g_thread_impl_vtable.DeleteConditionVariable)
282     g_thread_impl_vtable.DeleteConditionVariable (cond);
283 }
284
285 void
286 g_cond_signal (GCond *cond)
287 {
288   g_thread_impl_vtable.WakeConditionVariable (cond);
289 }
290
291 void
292 g_cond_broadcast (GCond *cond)
293 {
294   g_thread_impl_vtable.WakeAllConditionVariable (cond);
295 }
296
297 void
298 g_cond_wait (GCond  *cond,
299              GMutex *entered_mutex)
300 {
301   g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0);
302 }
303
304 gboolean
305 g_cond_timedwait (GCond  *cond,
306                   GMutex *entered_mutex,
307                   gint64  abs_time)
308 {
309   gint64 span;
310   FILETIME ft;
311   gint64 now;
312
313   GetSystemTimeAsFileTime (&ft);
314   memmove (&now, &ft, sizeof (FILETIME));
315
316   now -= G_GINT64_CONSTANT (116444736000000000);
317   now /= 10;
318
319   span = abs_time - now;
320
321   if G_UNLIKELY (span < 0)
322     span = 0;
323
324   if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32)
325     span = INFINITE;
326
327   return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0);
328 }
329
330 gboolean
331 g_cond_timed_wait (GCond    *cond,
332                    GMutex   *entered_mutex,
333                    GTimeVal *abs_time)
334 {
335   if (abs_time)
336     {
337       gint64 micros;
338
339       micros = abs_time->tv_sec;
340       micros *= 1000000;
341       micros += abs_time->tv_usec;
342
343       return g_cond_timedwait (cond, entered_mutex, micros);
344     }
345   else
346     {
347       g_cond_wait (cond, entered_mutex);
348       return TRUE;
349     }
350 }
351
352 /* {{{1 GPrivate */
353
354 typedef struct _GPrivateDestructor GPrivateDestructor;
355
356 struct _GPrivateDestructor
357 {
358   DWORD               index;
359   GDestroyNotify      notify;
360   GPrivateDestructor *next;
361 };
362
363 static GPrivateDestructor * volatile g_private_destructors;
364 static CRITICAL_SECTION g_private_lock;
365
366 static DWORD
367 g_private_get_impl (GPrivate *key)
368 {
369   DWORD impl = (DWORD) key->p;
370
371   if G_UNLIKELY (impl == 0)
372     {
373       EnterCriticalSection (&g_private_lock);
374       impl = (DWORD) key->p;
375       if (impl == 0)
376         {
377           GPrivateDestructor *destructor;
378
379           impl = TlsAlloc ();
380
381           if (impl == TLS_OUT_OF_INDEXES)
382             g_thread_abort (0, "TlsAlloc");
383
384           if (key->notify != NULL)
385             {
386               destructor = malloc (sizeof (GPrivateDestructor));
387               if G_UNLIKELY (destructor == NULL)
388                 g_thread_abort (errno, "malloc");
389               destructor->index = impl;
390               destructor->notify = key->notify;
391               destructor->next = g_private_destructors;
392
393               /* We need to do an atomic store due to the unlocked
394                * access to the destructor list from the thread exit
395                * function.
396                *
397                * It can double as a sanity check...
398                */
399               if (InterlockedCompareExchangePointer (&g_private_destructors, destructor,
400                                                      destructor->next) != destructor->next)
401                 g_thread_abort (0, "g_private_get_impl(1)");
402             }
403
404           /* Ditto, due to the unlocked access on the fast path */
405           if (InterlockedCompareExchangePointer (&key->p, impl, NULL) != NULL)
406             g_thread_abort (0, "g_private_get_impl(2)");
407         }
408       LeaveCriticalSection (&g_private_lock);
409     }
410
411   return impl;
412 }
413
414 gpointer
415 g_private_get (GPrivate *key)
416 {
417   return TlsGetValue (g_private_get_impl (key));
418 }
419
420 void
421 g_private_set (GPrivate *key,
422                gpointer  value)
423 {
424   TlsSetValue (g_private_get_impl (key), value);
425 }
426
427 void
428 g_private_replace (GPrivate *key,
429                    gpointer  value)
430 {
431   DWORD impl = g_private_get_impl (key);
432   gpointer old;
433
434   old = TlsGetValue (impl);
435   if (old && key->notify)
436     key->notify (old);
437   TlsSetValue (impl, value);
438 }
439
440 /* {{{1 GThread */
441
442 #include "glib.h"
443 #include "gthreadprivate.h"
444
445 #define win32_check_for_error(what) G_STMT_START{                       \
446   if (!(what))                                                          \
447     g_error ("file %s: line %d (%s): error %s during %s",               \
448              __FILE__, __LINE__, G_STRFUNC,                             \
449              g_win32_error_message (GetLastError ()), #what);           \
450   }G_STMT_END
451
452 #define G_MUTEX_SIZE (sizeof (gpointer))
453
454 static DWORD g_thread_self_tls;
455
456 typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
457
458 typedef struct _GThreadData GThreadData;
459 struct _GThreadData
460 {
461   GThreadFunc func;
462   gpointer data;
463   HANDLE thread;
464   gboolean joinable;
465 };
466
467 void
468 g_system_thread_self (gpointer thread)
469 {
470   GThreadData *self = TlsGetValue (g_thread_self_tls);
471
472   if (!self)
473     {
474       /* This should only happen for the main thread! */
475       HANDLE handle = GetCurrentThread ();
476       HANDLE process = GetCurrentProcess ();
477       self = g_new (GThreadData, 1);
478       win32_check_for_error (DuplicateHandle (process, handle, process,
479                                               &self->thread, 0, FALSE,
480                                               DUPLICATE_SAME_ACCESS));
481       win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
482       self->func = NULL;
483       self->data = NULL;
484       self->joinable = FALSE;
485     }
486
487   *(GThreadData **)thread = self;
488 }
489
490 void
491 g_system_thread_exit (void)
492 {
493   GThreadData *self = TlsGetValue (g_thread_self_tls);
494   gboolean dtors_called;
495
496   do
497     {
498       GPrivateDestructor *dtor;
499
500       /* We go by the POSIX book on this one.
501        *
502        * If we call a destructor then there is a chance that some new
503        * TLS variables got set by code called in that destructor.
504        *
505        * Loop until nothing is left.
506        */
507       dtors_called = FALSE;
508
509       for (dtor = g_private_destructors; dtor; dtor = dtor->next)
510         {
511           gpointer value;
512
513           value = TlsGetValue (dtor->index);
514           if (value != NULL && dtor->notify != NULL)
515             {
516               /* POSIX says to clear this before the call */
517               TlsSetValue (dtor->index, NULL);
518               dtor->notify (value);
519               dtors_called = TRUE;
520             }
521         }
522     }
523   while (dtors_called);
524
525   if (self)
526     {
527       if (!self->joinable)
528         {
529           win32_check_for_error (CloseHandle (self->thread));
530           g_free (self);
531         }
532       win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL));
533     }
534
535   if (g_thread_impl_vtable.CallThisOnThreadExit)
536     g_thread_impl_vtable.CallThisOnThreadExit ();
537
538   _endthreadex (0);
539 }
540
541 static guint __stdcall
542 g_thread_proxy (gpointer data)
543 {
544   GThreadData *self = (GThreadData*) data;
545
546   win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
547
548   self->func (self->data);
549
550   g_system_thread_exit ();
551
552   g_assert_not_reached ();
553
554   return 0;
555 }
556
557 void
558 g_system_thread_create (GThreadFunc       func,
559                         gpointer          data,
560                         gulong            stack_size,
561                         gboolean          joinable,
562                         gpointer          thread,
563                         GError          **error)
564 {
565   guint ignore;
566   GThreadData *retval;
567
568   g_return_if_fail (func);
569
570   retval = g_new(GThreadData, 1);
571   retval->func = func;
572   retval->data = data;
573
574   retval->joinable = joinable;
575
576   retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy,
577                                             retval, 0, &ignore);
578
579   if (retval->thread == NULL)
580     {
581       gchar *win_error = g_win32_error_message (GetLastError ());
582       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
583                    "Error creating thread: %s", win_error);
584       g_free (retval);
585       g_free (win_error);
586       return;
587     }
588
589   *(GThreadData **)thread = retval;
590 }
591
592 void
593 g_thread_yield (void)
594 {
595   Sleep(0);
596 }
597
598 void
599 g_system_thread_join (gpointer thread)
600 {
601   GThreadData *target = *(GThreadData **)thread;
602
603   g_return_if_fail (target->joinable);
604
605   win32_check_for_error (WAIT_FAILED !=
606                          WaitForSingleObject (target->thread, INFINITE));
607
608   win32_check_for_error (CloseHandle (target->thread));
609   g_free (target);
610 }
611
612 gboolean
613 g_system_thread_equal (gpointer thread1,
614                        gpointer thread2)
615 {
616    return ((GSystemThread*)thread1)->dummy_pointer == ((GSystemThread*)thread2)->dummy_pointer;
617 }
618
619 void
620 g_system_thread_set_name (const gchar *name)
621 {
622   /* FIXME: implement */
623 }
624
625 /* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
626
627 static CRITICAL_SECTION g_thread_xp_lock;
628 static DWORD            g_thread_xp_waiter_tls;
629
630 /* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
631 typedef struct _GThreadXpWaiter GThreadXpWaiter;
632 struct _GThreadXpWaiter
633 {
634   HANDLE                    event;
635   volatile GThreadXpWaiter *next;
636 };
637
638 static GThreadXpWaiter *
639 g_thread_xp_waiter_get (void)
640 {
641   GThreadXpWaiter *waiter;
642
643   waiter = TlsGetValue (g_thread_xp_waiter_tls);
644
645   if G_UNLIKELY (waiter == NULL)
646     {
647       waiter = malloc (sizeof (GThreadXpWaiter));
648       if (waiter == NULL)
649         g_thread_abort (GetLastError (), "malloc");
650       waiter->event = CreateEvent (0, FALSE, FALSE, NULL);
651       if (waiter->event == NULL)
652         g_thread_abort (GetLastError (), "CreateEvent");
653
654       TlsSetValue (g_thread_xp_waiter_tls, waiter);
655     }
656
657   return waiter;
658 }
659
660 static void __stdcall
661 g_thread_xp_CallThisOnThreadExit (void)
662 {
663   GThreadXpWaiter *waiter;
664
665   waiter = TlsGetValue (g_thread_xp_waiter_tls);
666
667   if (waiter != NULL)
668     {
669       TlsSetValue (g_thread_xp_waiter_tls, NULL);
670       CloseHandle (waiter->event);
671       free (waiter);
672     }
673 }
674
675 /* {{{2 SRWLock emulation */
676 typedef struct
677 {
678   CRITICAL_SECTION  writer_lock;
679   gboolean          ever_shared;    /* protected by writer_lock */
680   gboolean          writer_locked;  /* protected by writer_lock */
681
682   /* below is only ever touched if ever_shared becomes true */
683   CRITICAL_SECTION  atomicity;
684   GThreadXpWaiter  *queued_writer; /* protected by atomicity lock */
685   gint              num_readers;   /* protected by atomicity lock */
686 } GThreadSRWLock;
687
688 static void __stdcall
689 g_thread_xp_InitializeSRWLock (gpointer mutex)
690 {
691   *(GThreadSRWLock * volatile *) mutex = NULL;
692 }
693
694 static void __stdcall
695 g_thread_xp_DeleteSRWLock (gpointer mutex)
696 {
697   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
698
699   if (lock)
700     {
701       if (lock->ever_shared)
702         DeleteCriticalSection (&lock->atomicity);
703
704       DeleteCriticalSection (&lock->writer_lock);
705       free (lock);
706     }
707 }
708
709 static GThreadSRWLock * __stdcall
710 g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
711 {
712   GThreadSRWLock *result;
713
714   /* It looks like we're missing some barriers here, but this code only
715    * ever runs on Windows XP, which in turn only ever runs on hardware
716    * with a relatively rigid memory model.  The 'volatile' will take
717    * care of the compiler.
718    */
719   result = *lock;
720
721   if G_UNLIKELY (result == NULL)
722     {
723       EnterCriticalSection (&g_thread_xp_lock);
724
725       result = malloc (sizeof (GThreadSRWLock));
726
727       if (result == NULL)
728         g_thread_abort (errno, "malloc");
729
730       InitializeCriticalSection (&result->writer_lock);
731       result->writer_locked = FALSE;
732       result->ever_shared = FALSE;
733       *lock = result;
734
735       LeaveCriticalSection (&g_thread_xp_lock);
736     }
737
738   return result;
739 }
740
741 static void __stdcall
742 g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
743 {
744   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
745
746   EnterCriticalSection (&lock->writer_lock);
747
748   /* CRITICAL_SECTION is reentrant, but SRWLock is not.
749    * Detect the deadlock that would occur on later Windows version.
750    */
751   g_assert (!lock->writer_locked);
752   lock->writer_locked = TRUE;
753
754   if (lock->ever_shared)
755     {
756       GThreadXpWaiter *waiter = NULL;
757
758       EnterCriticalSection (&lock->atomicity);
759       if (lock->num_readers > 0)
760         lock->queued_writer = waiter = g_thread_xp_waiter_get ();
761       LeaveCriticalSection (&lock->atomicity);
762
763       if (waiter != NULL)
764         WaitForSingleObject (waiter->event, INFINITE);
765
766       lock->queued_writer = NULL;
767     }
768 }
769
770 static BOOLEAN __stdcall
771 g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
772 {
773   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
774
775   if (!TryEnterCriticalSection (&lock->writer_lock))
776     return FALSE;
777
778   /* CRITICAL_SECTION is reentrant, but SRWLock is not.
779    * Ensure that this properly returns FALSE (as SRWLock would).
780    */
781   if G_UNLIKELY (lock->writer_locked)
782     {
783       LeaveCriticalSection (&lock->writer_lock);
784       return FALSE;
785     }
786
787   lock->writer_locked = TRUE;
788
789   if (lock->ever_shared)
790     {
791       gboolean available;
792
793       EnterCriticalSection (&lock->atomicity);
794       available = lock->num_readers == 0;
795       LeaveCriticalSection (&lock->atomicity);
796
797       if (!available)
798         {
799           LeaveCriticalSection (&lock->writer_lock);
800           return FALSE;
801         }
802     }
803
804   return TRUE;
805 }
806
807 static void __stdcall
808 g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
809 {
810   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
811
812   lock->writer_locked = FALSE;
813
814   /* We need this until we fix some weird parts of GLib that try to
815    * unlock freshly-allocated mutexes.
816    */
817   if (lock != NULL)
818     LeaveCriticalSection (&lock->writer_lock);
819 }
820
821 static void
822 g_thread_xp_srwlock_become_reader (GThreadSRWLock *lock)
823 {
824   if G_UNLIKELY (!lock->ever_shared)
825     {
826       InitializeCriticalSection (&lock->atomicity);
827       lock->queued_writer = NULL;
828       lock->num_readers = 0;
829
830       lock->ever_shared = TRUE;
831     }
832
833   EnterCriticalSection (&lock->atomicity);
834   lock->num_readers++;
835   LeaveCriticalSection (&lock->atomicity);
836 }
837
838 static void __stdcall
839 g_thread_xp_AcquireSRWLockShared (gpointer mutex)
840 {
841   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
842
843   EnterCriticalSection (&lock->writer_lock);
844
845   /* See g_thread_xp_AcquireSRWLockExclusive */
846   g_assert (!lock->writer_locked);
847
848   g_thread_xp_srwlock_become_reader (lock);
849
850   LeaveCriticalSection (&lock->writer_lock);
851 }
852
853 static BOOLEAN __stdcall
854 g_thread_xp_TryAcquireSRWLockShared (gpointer mutex)
855 {
856   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
857
858   if (!TryEnterCriticalSection (&lock->writer_lock))
859     return FALSE;
860
861   /* See g_thread_xp_AcquireSRWLockExclusive */
862   if G_UNLIKELY (lock->writer_locked)
863     {
864       LeaveCriticalSection (&lock->writer_lock);
865       return FALSE;
866     }
867
868   g_thread_xp_srwlock_become_reader (lock);
869
870   LeaveCriticalSection (&lock->writer_lock);
871
872   return TRUE;
873 }
874
875 static void __stdcall
876 g_thread_xp_ReleaseSRWLockShared (gpointer mutex)
877 {
878   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
879
880   EnterCriticalSection (&lock->atomicity);
881
882   lock->num_readers--;
883
884   if (lock->num_readers == 0 && lock->queued_writer)
885     SetEvent (lock->queued_writer->event);
886
887   LeaveCriticalSection (&lock->atomicity);
888 }
889
890 /* {{{2 CONDITION_VARIABLE emulation */
891 typedef struct
892 {
893   volatile GThreadXpWaiter  *first;
894   volatile GThreadXpWaiter **last_ptr;
895 } GThreadXpCONDITION_VARIABLE;
896
897 static void __stdcall
898 g_thread_xp_InitializeConditionVariable (gpointer cond)
899 {
900   *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL;
901 }
902
903 static void __stdcall
904 g_thread_xp_DeleteConditionVariable (gpointer cond)
905 {
906   GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond;
907
908   if (cv)
909     free (cv);
910 }
911
912 static GThreadXpCONDITION_VARIABLE * __stdcall
913 g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond)
914 {
915   GThreadXpCONDITION_VARIABLE *result;
916
917   /* It looks like we're missing some barriers here, but this code only
918    * ever runs on Windows XP, which in turn only ever runs on hardware
919    * with a relatively rigid memory model.  The 'volatile' will take
920    * care of the compiler.
921    */
922   result = *cond;
923
924   if G_UNLIKELY (result == NULL)
925     {
926       result = malloc (sizeof (GThreadXpCONDITION_VARIABLE));
927
928       if (result == NULL)
929         g_thread_abort (errno, "malloc");
930
931       result->first = NULL;
932       result->last_ptr = &result->first;
933
934       if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL)
935         {
936           free (result);
937           result = *cond;
938         }
939     }
940
941   return result;
942 }
943
944 static BOOL __stdcall
945 g_thread_xp_SleepConditionVariableSRW (gpointer cond,
946                                        gpointer mutex,
947                                        DWORD    timeout,
948                                        ULONG    flags)
949 {
950   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
951   GThreadXpWaiter *waiter = g_thread_xp_waiter_get ();
952   DWORD status;
953
954   waiter->next = NULL;
955
956   EnterCriticalSection (&g_thread_xp_lock);
957   *cv->last_ptr = waiter;
958   cv->last_ptr = &waiter->next;
959   LeaveCriticalSection (&g_thread_xp_lock);
960
961   g_mutex_unlock (mutex);
962   status = WaitForSingleObject (waiter->event, timeout);
963
964   if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0)
965     g_thread_abort (GetLastError (), "WaitForSingleObject");
966
967   g_mutex_lock (mutex);
968
969   return status == WAIT_OBJECT_0;
970 }
971
972 static void __stdcall
973 g_thread_xp_WakeConditionVariable (gpointer cond)
974 {
975   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
976   volatile GThreadXpWaiter *waiter;
977
978   EnterCriticalSection (&g_thread_xp_lock);
979   waiter = cv->first;
980   if (waiter != NULL)
981     {
982       cv->first = waiter->next;
983       if (cv->first == NULL)
984         cv->last_ptr = &cv->first;
985     }
986   LeaveCriticalSection (&g_thread_xp_lock);
987
988   if (waiter != NULL)
989     SetEvent (waiter->event);
990 }
991
992 static void __stdcall
993 g_thread_xp_WakeAllConditionVariable (gpointer cond)
994 {
995   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
996   volatile GThreadXpWaiter *waiter;
997
998   EnterCriticalSection (&g_thread_xp_lock);
999   waiter = cv->first;
1000   cv->first = NULL;
1001   cv->last_ptr = &cv->first;
1002   LeaveCriticalSection (&g_thread_xp_lock);
1003
1004   while (waiter != NULL)
1005     {
1006       volatile GThreadXpWaiter *next;
1007
1008       next = waiter->next;
1009       SetEvent (waiter->event);
1010       waiter = next;
1011     }
1012 }
1013
1014 /* {{{2 XP Setup */
1015 static void
1016 g_thread_xp_init (void)
1017 {
1018   static const GThreadImplVtable g_thread_xp_impl_vtable = {
1019     g_thread_xp_CallThisOnThreadExit,
1020     g_thread_xp_InitializeSRWLock,
1021     g_thread_xp_DeleteSRWLock,
1022     g_thread_xp_AcquireSRWLockExclusive,
1023     g_thread_xp_TryAcquireSRWLockExclusive,
1024     g_thread_xp_ReleaseSRWLockExclusive,
1025     g_thread_xp_AcquireSRWLockShared,
1026     g_thread_xp_TryAcquireSRWLockShared,
1027     g_thread_xp_ReleaseSRWLockShared,
1028     g_thread_xp_InitializeConditionVariable,
1029     g_thread_xp_DeleteConditionVariable,
1030     g_thread_xp_SleepConditionVariableSRW,
1031     g_thread_xp_WakeAllConditionVariable,
1032     g_thread_xp_WakeConditionVariable
1033   };
1034
1035   InitializeCriticalSection (&g_thread_xp_lock);
1036   g_thread_xp_waiter_tls = TlsAlloc ();
1037
1038   g_thread_impl_vtable = g_thread_xp_impl_vtable;
1039 }
1040
1041 /* {{{1 Epilogue */
1042
1043 static gboolean
1044 g_thread_lookup_native_funcs (void)
1045 {
1046   GThreadImplVtable native_vtable = { 0, };
1047   HMODULE kernel32;
1048
1049   kernel32 = GetModuleHandle ("KERNEL32.DLL");
1050
1051   if (kernel32 == NULL)
1052     return FALSE;
1053
1054 #define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE
1055   GET_FUNC(InitializeSRWLock);
1056   GET_FUNC(AcquireSRWLockExclusive);
1057   GET_FUNC(TryAcquireSRWLockExclusive);
1058   GET_FUNC(ReleaseSRWLockExclusive);
1059   GET_FUNC(AcquireSRWLockShared);
1060   GET_FUNC(TryAcquireSRWLockShared);
1061   GET_FUNC(ReleaseSRWLockShared);
1062
1063   GET_FUNC(InitializeConditionVariable);
1064   GET_FUNC(SleepConditionVariableSRW);
1065   GET_FUNC(WakeAllConditionVariable);
1066   GET_FUNC(WakeConditionVariable);
1067 #undef GET_FUNC
1068
1069   g_thread_impl_vtable = native_vtable;
1070
1071   return TRUE;
1072 }
1073
1074 G_GNUC_INTERNAL void
1075 g_thread_DllMain (void)
1076 {
1077   if (g_thread_lookup_native_funcs ())
1078     fprintf (stderr, "(debug) GThread using native mode\n");
1079   else
1080     {
1081       fprintf (stderr, "(debug) GThread using Windows XP mode\n");
1082       g_thread_xp_init ();
1083     }
1084
1085   win32_check_for_error (TLS_OUT_OF_INDEXES != (g_thread_self_tls = TlsAlloc ()));
1086   InitializeCriticalSection (&g_private_lock);
1087 }
1088
1089 /* vim:set foldmethod=marker: */
1090