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