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