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