Rework GMutex and GCond APIs
[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
47 #define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
48 #include <windows.h>
49
50 #include <process.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53
54 static void
55 g_thread_abort (gint         status,
56                 const gchar *function)
57 {
58   fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s.  Aborting.\n",
59            strerror (status), function);
60   abort ();
61 }
62
63 /* Starting with Vista and Windows 2008, we have access to the
64  * CONDITION_VARIABLE and SRWLock primatives on Windows, which are
65  * pretty reasonable approximations of the primatives specified in
66  * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively).
67  *
68  * Both of these types are structs containing a single pointer.  That
69  * pointer is used as an atomic bitfield to support user-space mutexes
70  * that only get the kernel involved in cases of contention (similar
71  * to how futex()-based mutexes work on Linux).  The biggest advantage
72  * of these new types is that they can be statically initialised to
73  * zero.  This allows us to use them directly and still support:
74  *
75  *   GMutex mutex = G_MUTEX_INIT;
76  *
77  * and
78  *
79  *   GCond cond = G_COND_INIT;
80  *
81  * Unfortunately, Windows XP lacks these facilities and GLib still
82  * needs to support Windows XP.  Our approach here is as follows:
83  *
84  *   - avoid depending on structure declarations at compile-time by
85  *     declaring our own GMutex and GCond strutures to be
86  *     ABI-compatible with SRWLock and CONDITION_VARIABLE and using
87  *     those instead
88  *
89  *   - avoid a hard dependency on the symbols used to manipulate these
90  *     structures by doing a dynamic lookup of those symbols at
91  *     runtime
92  *
93  *   - if the symbols are not available, emulate them using other
94  *     primatives
95  *
96  * Using this approach also allows us to easily build a GLib that lacks
97  * support for Windows XP or to remove this code entirely when XP is no
98  * longer supported (end of line is currently April 8, 2014).
99  */
100 typedef struct
101 {
102   void     (* CallThisOnThreadExit)        (void);              /* fake */
103
104   void     (* InitializeSRWLock)           (gpointer lock);
105   void     (* DeleteSRWLock)               (gpointer lock);     /* fake */
106   void     (* AcquireSRWLockExclusive)     (gpointer lock);
107   BOOLEAN  (* TryAcquireSRWLockExclusive)  (gpointer lock);
108   void     (* ReleaseSRWLockExclusive)     (gpointer lock);
109
110   void     (* InitializeConditionVariable) (gpointer cond);
111   void     (* DeleteConditionVariable)     (gpointer cond);     /* fake */
112   BOOL     (* SleepConditionVariableSRW)   (gpointer cond,
113                                             gpointer lock,
114                                             DWORD    timeout,
115                                             ULONG    flags);
116   void     (* WakeAllConditionVariable)    (gpointer cond);
117   void     (* 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   /* temporary until we fix libglib */
140   if (mutex == NULL)
141     return;
142
143   g_thread_impl_vtable.AcquireSRWLockExclusive (mutex);
144 }
145
146 gboolean
147 g_mutex_trylock (GMutex *mutex)
148 {
149   /* temporary until we fix libglib */
150   if (mutex == NULL)
151     return TRUE;
152
153   return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex);
154 }
155
156 void
157 g_mutex_unlock (GMutex *mutex)
158 {
159   /* temporary until we fix libglib */
160   if (mutex == NULL)
161     return;
162
163   g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex);
164 }
165
166 /* {{{1 GCond */
167 void
168 g_cond_init (GCond *cond)
169 {
170   g_thread_impl_vtable.InitializeConditionVariable (cond);
171 }
172
173 void
174 g_cond_clear (GCond *cond)
175 {
176   if (g_thread_impl_vtable.DeleteConditionVariable)
177     g_thread_impl_vtable.DeleteConditionVariable (cond);
178 }
179
180 void
181 g_cond_signal (GCond *cond)
182 {
183   /* temporary until we fix libglib */
184   if (cond == NULL)
185     return;
186
187   g_thread_impl_vtable.WakeConditionVariable (cond);
188 }
189
190 void
191 g_cond_broadcast (GCond *cond)
192 {
193   /* temporary until we fix libglib */
194   if (cond == NULL)
195     return;
196
197   g_thread_impl_vtable.WakeAllConditionVariable (cond);
198 }
199
200 void
201 g_cond_wait (GCond  *cond,
202              GMutex *entered_mutex)
203 {
204   g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0);
205 }
206
207 gboolean
208 g_cond_timedwait (GCond  *cond,
209                   GMutex *entered_mutex,
210                   gint64  abs_time)
211 {
212   gint64 span;
213   FILETIME ft;
214   gint64 now;
215
216   GetSystemTimeAsFileTime (&ft);
217   memmove (&now, &ft, sizeof (FILETIME));
218
219   now -= G_GINT64_CONSTANT (116444736000000000);
220   now /= 10;
221
222   span = abs_time - now;
223
224   if G_UNLIKELY (span < 0)
225     span = 0;
226
227   if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32)
228     span = INFINITE;
229
230   return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0);
231 }
232
233 gboolean
234 g_cond_timed_wait (GCond    *cond,
235                    GMutex   *entered_mutex,
236                    GTimeVal *abs_time)
237 {
238   if (abs_time)
239     {
240       gint64 micros;
241
242       micros = abs_time->tv_sec;
243       micros *= 1000000;
244       micros += abs_time->tv_usec;
245
246       return g_cond_timedwait (cond, entered_mutex, micros);
247     }
248   else
249     {
250       g_cond_wait (cond, entered_mutex);
251       return TRUE;
252     }
253 }
254
255 /* {{{1 new/free API */
256 GMutex *
257 g_mutex_new (void)
258 {
259   GMutex *mutex;
260
261   /* malloc() is temporary until all libglib users are ported away */
262   mutex = malloc (sizeof (GMutex));
263   if G_UNLIKELY (mutex == NULL)
264     g_thread_abort (errno, "malloc");
265   g_mutex_init (mutex);
266
267   return mutex;
268 }
269
270 void
271 g_mutex_free (GMutex *mutex)
272 {
273   g_mutex_clear (mutex);
274   free (mutex);
275 }
276
277 GCond *
278 g_cond_new (void)
279 {
280   GCond *cond;
281
282   /* malloc() is temporary until all libglib users are ported away */
283   cond = malloc (sizeof (GCond));
284   if G_UNLIKELY (cond == NULL)
285     g_thread_abort (errno, "malloc");
286   g_cond_init (cond);
287
288   return cond;
289 }
290
291 void
292 g_cond_free (GCond *cond)
293 {
294   g_cond_clear (cond);
295   free (cond);
296 }
297
298 /* {{{1 GPrivate */
299
300 #include "glib.h"
301 #include "gthreadprivate.h"
302
303 #define win32_check_for_error(what) G_STMT_START{                       \
304   if (!(what))                                                          \
305     g_error ("file %s: line %d (%s): error %s during %s",               \
306              __FILE__, __LINE__, G_STRFUNC,                             \
307              g_win32_error_message (GetLastError ()), #what);           \
308   }G_STMT_END
309
310 #define G_MUTEX_SIZE (sizeof (gpointer))
311
312 static DWORD g_thread_self_tls;
313 static DWORD g_private_tls;
314 static CRITICAL_SECTION g_thread_global_spinlock;
315
316 typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
317
318 /* As noted in the docs, GPrivate is a limited resource, here we take
319  * a rather low maximum to save memory, use GStaticPrivate instead. */
320 #define G_PRIVATE_MAX 100
321
322 static GDestroyNotify g_private_destructors[G_PRIVATE_MAX];
323
324 static guint g_private_next = 0;
325
326 typedef struct _GThreadData GThreadData;
327 struct _GThreadData
328 {
329   GThreadFunc func;
330   gpointer data;
331   HANDLE thread;
332   gboolean joinable;
333 };
334
335 static GPrivate *
336 g_private_new_win32_impl (GDestroyNotify destructor)
337 {
338   GPrivate *result;
339   EnterCriticalSection (&g_thread_global_spinlock);
340   if (g_private_next >= G_PRIVATE_MAX)
341     {
342       char buf[100];
343       sprintf (buf,
344                "Too many GPrivate allocated. Their number is limited to %d.",
345                G_PRIVATE_MAX);
346       MessageBox (NULL, buf, NULL, MB_ICONERROR|MB_SETFOREGROUND);
347       if (IsDebuggerPresent ())
348         G_BREAKPOINT ();
349       abort ();
350     }
351   g_private_destructors[g_private_next] = destructor;
352   result = GUINT_TO_POINTER (g_private_next);
353   g_private_next++;
354   LeaveCriticalSection (&g_thread_global_spinlock);
355
356   return result;
357 }
358
359 /* NOTE: the functions g_private_get and g_private_set may not use
360    functions from gmem.c and gmessages.c */
361
362 static void
363 g_private_set_win32_impl (GPrivate * private_key, gpointer value)
364 {
365   gpointer* array = TlsGetValue (g_private_tls);
366   guint index = GPOINTER_TO_UINT (private_key);
367
368   if (index >= G_PRIVATE_MAX)
369       return;
370
371   if (!array)
372     {
373       array = (gpointer*) calloc (G_PRIVATE_MAX, sizeof (gpointer));
374       TlsSetValue (g_private_tls, array);
375     }
376
377   array[index] = value;
378 }
379
380 static gpointer
381 g_private_get_win32_impl (GPrivate * private_key)
382 {
383   gpointer* array = TlsGetValue (g_private_tls);
384   guint index = GPOINTER_TO_UINT (private_key);
385
386   if (index >= G_PRIVATE_MAX || !array)
387     return NULL;
388
389   return array[index];
390 }
391
392 /* {{{1 GThread */
393
394 static void
395 g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority)
396 {
397   GThreadData *target = *(GThreadData **)thread;
398   gint native_prio;
399
400   switch (priority)
401     {
402     case G_THREAD_PRIORITY_LOW:
403       native_prio = THREAD_PRIORITY_BELOW_NORMAL;
404       break;
405
406     case G_THREAD_PRIORITY_NORMAL:
407       native_prio = THREAD_PRIORITY_NORMAL;
408       break;
409
410     case G_THREAD_PRIORITY_HIGH:
411       native_prio = THREAD_PRIORITY_ABOVE_NORMAL;
412       break;
413
414     case G_THREAD_PRIORITY_URGENT:
415       native_prio = THREAD_PRIORITY_HIGHEST;
416       break;
417
418     default:
419       g_return_if_reached ();
420     }
421
422   win32_check_for_error (SetThreadPriority (target->thread, native_prio));
423 }
424
425 static void
426 g_thread_self_win32_impl (gpointer thread)
427 {
428   GThreadData *self = TlsGetValue (g_thread_self_tls);
429
430   if (!self)
431     {
432       /* This should only happen for the main thread! */
433       HANDLE handle = GetCurrentThread ();
434       HANDLE process = GetCurrentProcess ();
435       self = g_new (GThreadData, 1);
436       win32_check_for_error (DuplicateHandle (process, handle, process,
437                                               &self->thread, 0, FALSE,
438                                               DUPLICATE_SAME_ACCESS));
439       win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
440       self->func = NULL;
441       self->data = NULL;
442       self->joinable = FALSE;
443     }
444
445   *(GThreadData **)thread = self;
446 }
447
448 static void
449 g_thread_exit_win32_impl (void)
450 {
451   GThreadData *self = TlsGetValue (g_thread_self_tls);
452   guint i, private_max;
453   gpointer *array = TlsGetValue (g_private_tls);
454
455   EnterCriticalSection (&g_thread_global_spinlock);
456   private_max = g_private_next;
457   LeaveCriticalSection (&g_thread_global_spinlock);
458
459   if (array)
460     {
461       gboolean some_data_non_null;
462
463       do {
464         some_data_non_null = FALSE;
465         for (i = 0; i < private_max; i++)
466           {
467             GDestroyNotify destructor = g_private_destructors[i];
468             GDestroyNotify data = array[i];
469
470             if (data)
471               some_data_non_null = TRUE;
472
473             array[i] = NULL;
474
475             if (destructor && data)
476               destructor (data);
477           }
478       } while (some_data_non_null);
479
480       free (array);
481
482       win32_check_for_error (TlsSetValue (g_private_tls, NULL));
483     }
484
485   if (self)
486     {
487       if (!self->joinable)
488         {
489           win32_check_for_error (CloseHandle (self->thread));
490           g_free (self);
491         }
492       win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL));
493     }
494
495   if (g_thread_impl_vtable.CallThisOnThreadExit)
496     g_thread_impl_vtable.CallThisOnThreadExit ();
497
498   _endthreadex (0);
499 }
500
501 static guint __stdcall
502 g_thread_proxy (gpointer data)
503 {
504   GThreadData *self = (GThreadData*) data;
505
506   win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
507
508   self->func (self->data);
509
510   g_thread_exit_win32_impl ();
511
512   g_assert_not_reached ();
513
514   return 0;
515 }
516
517 static void
518 g_thread_create_win32_impl (GThreadFunc func,
519                             gpointer data,
520                             gulong stack_size,
521                             gboolean joinable,
522                             gboolean bound,
523                             GThreadPriority priority,
524                             gpointer thread,
525                             GError **error)
526 {
527   guint ignore;
528   GThreadData *retval;
529
530   g_return_if_fail (func);
531   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
532   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
533
534   retval = g_new(GThreadData, 1);
535   retval->func = func;
536   retval->data = data;
537
538   retval->joinable = joinable;
539
540   retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy,
541                                             retval, 0, &ignore);
542
543   if (retval->thread == NULL)
544     {
545       gchar *win_error = g_win32_error_message (GetLastError ());
546       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
547                    "Error creating thread: %s", win_error);
548       g_free (retval);
549       g_free (win_error);
550       return;
551     }
552
553   *(GThreadData **)thread = retval;
554
555   g_thread_set_priority_win32_impl (thread, priority);
556 }
557
558 static void
559 g_thread_yield_win32_impl (void)
560 {
561   Sleep(0);
562 }
563
564 static void
565 g_thread_join_win32_impl (gpointer thread)
566 {
567   GThreadData *target = *(GThreadData **)thread;
568
569   g_return_if_fail (target->joinable);
570
571   win32_check_for_error (WAIT_FAILED !=
572                          WaitForSingleObject (target->thread, INFINITE));
573
574   win32_check_for_error (CloseHandle (target->thread));
575   g_free (target);
576 }
577
578 /* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
579
580 static DWORD            g_thread_xp_waiter_tls;
581 static CRITICAL_SECTION g_thread_xp_lock;
582
583 /* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
584 typedef struct _GThreadXpWaiter GThreadXpWaiter;
585 struct _GThreadXpWaiter
586 {
587   HANDLE                    event;
588   volatile GThreadXpWaiter *next;
589 };
590
591 static GThreadXpWaiter *
592 g_thread_xp_waiter_get (void)
593 {
594   GThreadXpWaiter *waiter;
595
596   waiter = TlsGetValue (g_thread_xp_waiter_tls);
597
598   if G_UNLIKELY (waiter == NULL)
599     {
600       waiter = malloc (sizeof (GThreadXpWaiter));
601       if (waiter == NULL)
602         g_thread_abort (GetLastError (), "malloc");
603       waiter->event = CreateEvent (0, FALSE, FALSE, NULL);
604       if (waiter->event == NULL)
605         g_thread_abort (GetLastError (), "CreateEvent");
606
607       TlsSetValue (g_thread_xp_waiter_tls, waiter);
608     }
609
610   return waiter;
611 }
612
613 static void
614 g_thread_xp_CallThisOnThreadExit (void)
615 {
616   GThreadXpWaiter *waiter;
617
618   waiter = TlsGetValue (g_thread_xp_waiter_tls);
619
620   if (waiter != NULL)
621     {
622       TlsSetValue (g_thread_xp_waiter_tls, NULL);
623       CloseHandle (waiter->event);
624       free (waiter);
625     }
626 }
627
628 /* {{{2 SRWLock emulation */
629 typedef struct
630 {
631   CRITICAL_SECTION critical_section;
632 } GThreadSRWLock;
633
634 static void
635 g_thread_xp_InitializeSRWLock (gpointer mutex)
636 {
637   *(GThreadSRWLock * volatile *) mutex = NULL;
638 }
639
640 static void
641 g_thread_xp_DeleteSRWLock (gpointer mutex)
642 {
643   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
644
645   if (lock)
646     {
647       DeleteCriticalSection (&lock->critical_section);
648       free (lock);
649     }
650 }
651
652 static GThreadSRWLock *
653 g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
654 {
655   GThreadSRWLock *result;
656
657   /* It looks like we're missing some barriers here, but this code only
658    * ever runs on Windows XP, which in turn only ever runs on hardware
659    * with a relatively rigid memory model.  The 'volatile' will take
660    * care of the compiler.
661    */
662   result = *lock;
663
664   if G_UNLIKELY (result == NULL)
665     {
666       EnterCriticalSection (&g_thread_xp_lock);
667
668       result = malloc (sizeof (GThreadSRWLock));
669
670       if (result == NULL)
671         g_thread_abort (errno, "malloc");
672
673       InitializeCriticalSection (&result->critical_section);
674       *lock = result;
675
676       LeaveCriticalSection (&g_thread_xp_lock);
677     }
678
679   return result;
680 }
681
682 static void
683 g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
684 {
685   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
686
687   EnterCriticalSection (&lock->critical_section);
688 }
689
690 static BOOLEAN
691 g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
692 {
693   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
694
695   return TryEnterCriticalSection (&lock->critical_section);
696 }
697
698 static void
699 g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
700 {
701   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
702
703   /* We need this until we fix some weird parts of GLib that try to
704    * unlock freshly-allocated mutexes.
705    */
706   if (lock != NULL)
707     LeaveCriticalSection (&lock->critical_section);
708 }
709
710 /* {{{2 CONDITION_VARIABLE emulation */
711 typedef struct
712 {
713   volatile GThreadXpWaiter  *first;
714   volatile GThreadXpWaiter **last_ptr;
715 } GThreadXpCONDITION_VARIABLE;
716
717 static void
718 g_thread_xp_InitializeConditionVariable (gpointer cond)
719 {
720   *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL;
721 }
722
723 static void
724 g_thread_xp_DeleteConditionVariable (gpointer cond)
725 {
726   GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond;
727
728   if (cv)
729     free (cv);
730 }
731
732 static GThreadXpCONDITION_VARIABLE *
733 g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond)
734 {
735   GThreadXpCONDITION_VARIABLE *result;
736
737   /* It looks like we're missing some barriers here, but this code only
738    * ever runs on Windows XP, which in turn only ever runs on hardware
739    * with a relatively rigid memory model.  The 'volatile' will take
740    * care of the compiler.
741    */
742   result = *cond;
743
744   if G_UNLIKELY (result == NULL)
745     {
746       result = malloc (sizeof (GThreadXpCONDITION_VARIABLE));
747
748       if (result == NULL)
749         g_thread_abort (errno, "malloc");
750
751       result->first = NULL;
752       result->last_ptr = &result->first;
753
754       if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL)
755         {
756           free (result);
757           result = *cond;
758         }
759     }
760
761   return result;
762 }
763
764 static BOOL
765 g_thread_xp_SleepConditionVariableSRW (gpointer cond,
766                                        gpointer mutex,
767                                        DWORD    timeout,
768                                        ULONG    flags)
769 {
770   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
771   GThreadXpWaiter *waiter = g_thread_xp_waiter_get ();
772   DWORD status;
773
774   waiter->next = NULL;
775
776   EnterCriticalSection (&g_thread_xp_lock);
777   *cv->last_ptr = waiter;
778   cv->last_ptr = &waiter->next;
779   LeaveCriticalSection (&g_thread_xp_lock);
780
781   g_mutex_unlock (mutex);
782   status = WaitForSingleObject (waiter->event, timeout);
783
784   if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0)
785     g_thread_abort (GetLastError (), "WaitForSingleObject");
786
787   g_mutex_lock (mutex);
788
789   return status == WAIT_OBJECT_0;
790 }
791
792 static void
793 g_thread_xp_WakeConditionVariable (gpointer cond)
794 {
795   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
796   volatile GThreadXpWaiter *waiter;
797
798   EnterCriticalSection (&g_thread_xp_lock);
799   waiter = cv->first;
800   if (waiter != NULL)
801     {
802       cv->first = waiter->next;
803       if (cv->first == NULL)
804         cv->last_ptr = &cv->first;
805     }
806   LeaveCriticalSection (&g_thread_xp_lock);
807
808   if (waiter != NULL)
809     SetEvent (waiter->event);
810 }
811
812 static void
813 g_thread_xp_WakeAllConditionVariable (gpointer cond)
814 {
815   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
816   volatile GThreadXpWaiter *waiter;
817
818   EnterCriticalSection (&g_thread_xp_lock);
819   waiter = cv->first;
820   cv->first = NULL;
821   cv->last_ptr = &cv->first;
822   LeaveCriticalSection (&g_thread_xp_lock);
823
824   while (waiter != NULL)
825     {
826       volatile GThreadXpWaiter *next;
827
828       next = waiter->next;
829       SetEvent (waiter->event);
830       waiter = next;
831     }
832 }
833
834 /* {{{2 XP Setup */
835 static void
836 g_thread_xp_init (void)
837 {
838   static const GThreadImplVtable g_thread_xp_impl_vtable = {
839     g_thread_xp_CallThisOnThreadExit,
840     g_thread_xp_InitializeSRWLock,
841     g_thread_xp_DeleteSRWLock,
842     g_thread_xp_AcquireSRWLockExclusive,
843     g_thread_xp_TryAcquireSRWLockExclusive,
844     g_thread_xp_ReleaseSRWLockExclusive,
845     g_thread_xp_InitializeConditionVariable,
846     g_thread_xp_DeleteConditionVariable,
847     g_thread_xp_SleepConditionVariableSRW,
848     g_thread_xp_WakeAllConditionVariable,
849     g_thread_xp_WakeConditionVariable
850   };
851
852   InitializeCriticalSection (&g_thread_xp_lock);
853   g_thread_xp_waiter_tls = TlsAlloc ();
854
855   g_thread_impl_vtable = g_thread_xp_impl_vtable;
856 }
857
858 /* {{{1 Epilogue */
859
860 GThreadFunctions g_thread_functions_for_glib_use =
861 {
862   g_mutex_new,           /* mutex */
863   g_mutex_lock,
864   g_mutex_trylock,
865   g_mutex_unlock,
866   g_mutex_free,
867   g_cond_new,            /* condition */
868   g_cond_signal,
869   g_cond_broadcast,
870   g_cond_wait,
871   g_cond_timed_wait,
872   g_cond_free,
873   g_private_new_win32_impl,         /* private thread data */
874   g_private_get_win32_impl,
875   g_private_set_win32_impl,
876   g_thread_create_win32_impl,       /* thread */
877   g_thread_yield_win32_impl,
878   g_thread_join_win32_impl,
879   g_thread_exit_win32_impl,
880   g_thread_set_priority_win32_impl,
881   g_thread_self_win32_impl,
882   NULL                             /* no equal function necessary */
883 };
884
885 void
886 _g_thread_impl_init (void)
887 {
888   static gboolean beenhere = FALSE;
889
890   if (beenhere)
891     return;
892
893   beenhere = TRUE;
894
895   printf ("thread init\n");
896   win32_check_for_error (TLS_OUT_OF_INDEXES !=
897                          (g_thread_self_tls = TlsAlloc ()));
898   win32_check_for_error (TLS_OUT_OF_INDEXES !=
899                          (g_private_tls = TlsAlloc ()));
900   InitializeCriticalSection (&g_thread_global_spinlock);
901 }
902
903 static gboolean
904 g_thread_lookup_native_funcs (void)
905 {
906   GThreadImplVtable native_vtable = { 0, };
907   HMODULE kernel32;
908
909   kernel32 = GetModuleHandle ("KERNEL32.DLL");
910
911   if (kernel32 == NULL)
912     return FALSE;
913
914 #define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE
915   GET_FUNC(InitializeSRWLock);
916   GET_FUNC(AcquireSRWLockExclusive);
917   GET_FUNC(TryAcquireSRWLockExclusive);
918   GET_FUNC(ReleaseSRWLockExclusive);
919
920   GET_FUNC(InitializeConditionVariable);
921   GET_FUNC(SleepConditionVariableSRW);
922   GET_FUNC(WakeAllConditionVariable);
923   GET_FUNC(WakeConditionVariable);
924 #undef GET_FUNC
925
926   g_thread_impl_vtable = native_vtable;
927
928   return TRUE;
929 }
930
931 G_GNUC_INTERNAL void
932 g_thread_DllMain (void)
933 {
934   /* XXX This is broken right now for some unknown reason...
935
936   if (g_thread_lookup_native_funcs ())
937     fprintf (stderr, "(debug) GThread using native mode\n");
938   else
939 */
940     {
941       fprintf (stderr, "(debug) GThread using Windows XP mode\n");
942       g_thread_xp_init ();
943     }
944 }
945
946 /* vim:set foldmethod=marker: */
947