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