Rework GPrivate
[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
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   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 GCond */
155 void
156 g_cond_init (GCond *cond)
157 {
158   g_thread_impl_vtable.InitializeConditionVariable (cond);
159 }
160
161 void
162 g_cond_clear (GCond *cond)
163 {
164   if (g_thread_impl_vtable.DeleteConditionVariable)
165     g_thread_impl_vtable.DeleteConditionVariable (cond);
166 }
167
168 void
169 g_cond_signal (GCond *cond)
170 {
171   g_thread_impl_vtable.WakeConditionVariable (cond);
172 }
173
174 void
175 g_cond_broadcast (GCond *cond)
176 {
177   g_thread_impl_vtable.WakeAllConditionVariable (cond);
178 }
179
180 void
181 g_cond_wait (GCond  *cond,
182              GMutex *entered_mutex)
183 {
184   g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0);
185 }
186
187 gboolean
188 g_cond_timedwait (GCond  *cond,
189                   GMutex *entered_mutex,
190                   gint64  abs_time)
191 {
192   gint64 span;
193   FILETIME ft;
194   gint64 now;
195
196   GetSystemTimeAsFileTime (&ft);
197   memmove (&now, &ft, sizeof (FILETIME));
198
199   now -= G_GINT64_CONSTANT (116444736000000000);
200   now /= 10;
201
202   span = abs_time - now;
203
204   if G_UNLIKELY (span < 0)
205     span = 0;
206
207   if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32)
208     span = INFINITE;
209
210   return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0);
211 }
212
213 gboolean
214 g_cond_timed_wait (GCond    *cond,
215                    GMutex   *entered_mutex,
216                    GTimeVal *abs_time)
217 {
218   if (abs_time)
219     {
220       gint64 micros;
221
222       micros = abs_time->tv_sec;
223       micros *= 1000000;
224       micros += abs_time->tv_usec;
225
226       return g_cond_timedwait (cond, entered_mutex, micros);
227     }
228   else
229     {
230       g_cond_wait (cond, entered_mutex);
231       return TRUE;
232     }
233 }
234
235 /* {{{1 GPrivate */
236
237 typedef struct _GPrivateDestructor GPrivateDestructor;
238
239 struct _GPrivateDestructor
240 {
241   DWORD               index;
242   GDestroyNotify      notify;
243   GPrivateDestructor *next;
244 };
245
246 static GPrivateDestructor * volatile g_private_destructors;
247
248 GPrivate *
249 (g_private_new) (GDestroyNotify notify)
250 {
251   GPrivate *key;
252
253   key = malloc (sizeof (GPrivate));
254   if G_UNLIKELY (key == NULL)
255     g_thread_abort (errno, "malloc");
256   g_private_init (key, notify);
257
258   return key;
259 }
260
261 void
262 g_private_init (GPrivate       *key,
263                 GDestroyNotify  notify)
264 {
265   GPrivateDestructor *destructor;
266
267   key->index = TlsAlloc ();
268
269   destructor = malloc (sizeof (GPrivateDestructor));
270   if G_UNLIKELY (destructor == NULL)
271     g_thread_abort (errno, "malloc");
272   destructor->index = key->index;
273   destructor->notify = notify;
274
275   do
276     destructor->next = g_private_destructors;
277   while (InterlockedCompareExchangePointer (&g_private_destructors, destructor->next, destructor) != destructor->next);
278 }
279
280 gpointer
281 (g_private_get) (GPrivate *key)
282 {
283   return TlsGetValue (key->index);
284 }
285
286 void
287 (g_private_set) (GPrivate *key,
288                gpointer  value)
289 {
290   TlsSetValue (key->index, value);
291 }
292
293 /* {{{1 GThread */
294
295 #include "glib.h"
296 #include "gthreadprivate.h"
297
298 #define win32_check_for_error(what) G_STMT_START{                       \
299   if (!(what))                                                          \
300     g_error ("file %s: line %d (%s): error %s during %s",               \
301              __FILE__, __LINE__, G_STRFUNC,                             \
302              g_win32_error_message (GetLastError ()), #what);           \
303   }G_STMT_END
304
305 #define G_MUTEX_SIZE (sizeof (gpointer))
306
307 static DWORD g_thread_self_tls;
308 static DWORD g_private_tls;
309
310 typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
311
312 typedef struct _GThreadData GThreadData;
313 struct _GThreadData
314 {
315   GThreadFunc func;
316   gpointer data;
317   HANDLE thread;
318   gboolean joinable;
319 };
320
321
322 static void
323 g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority)
324 {
325   GThreadData *target = *(GThreadData **)thread;
326   gint native_prio;
327
328   switch (priority)
329     {
330     case G_THREAD_PRIORITY_LOW:
331       native_prio = THREAD_PRIORITY_BELOW_NORMAL;
332       break;
333
334     case G_THREAD_PRIORITY_NORMAL:
335       native_prio = THREAD_PRIORITY_NORMAL;
336       break;
337
338     case G_THREAD_PRIORITY_HIGH:
339       native_prio = THREAD_PRIORITY_ABOVE_NORMAL;
340       break;
341
342     case G_THREAD_PRIORITY_URGENT:
343       native_prio = THREAD_PRIORITY_HIGHEST;
344       break;
345
346     default:
347       g_return_if_reached ();
348     }
349
350   win32_check_for_error (SetThreadPriority (target->thread, native_prio));
351 }
352
353 static void
354 g_thread_self_win32_impl (gpointer thread)
355 {
356   GThreadData *self = TlsGetValue (g_thread_self_tls);
357
358   if (!self)
359     {
360       /* This should only happen for the main thread! */
361       HANDLE handle = GetCurrentThread ();
362       HANDLE process = GetCurrentProcess ();
363       self = g_new (GThreadData, 1);
364       win32_check_for_error (DuplicateHandle (process, handle, process,
365                                               &self->thread, 0, FALSE,
366                                               DUPLICATE_SAME_ACCESS));
367       win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
368       self->func = NULL;
369       self->data = NULL;
370       self->joinable = FALSE;
371     }
372
373   *(GThreadData **)thread = self;
374 }
375
376 static void
377 g_thread_exit_win32_impl (void)
378 {
379   GThreadData *self = TlsGetValue (g_thread_self_tls);
380   gboolean dtors_called;
381
382   do
383     {
384       GPrivateDestructor *dtor;
385
386       /* We go by the POSIX book on this one.
387        *
388        * If we call a destructor then there is a chance that some new
389        * TLS variables got set by code called in that destructor.
390        *
391        * Loop until nothing is left.
392        */
393       dtors_called = FALSE;
394
395       for (dtor = g_private_destructors; dtor; dtor = dtor->next)
396         {
397           gpointer value;
398
399           value = TlsGetValue (dtor->index);
400           if (value != NULL && dtor->notify != NULL)
401             {
402               /* POSIX says to clear this before the call */
403               TlsSetValue (dtor->index, NULL);
404               dtor->notify (value);
405               dtors_called = TRUE;
406             }
407         }
408     }
409   while (dtors_called);
410
411   if (self)
412     {
413       if (!self->joinable)
414         {
415           win32_check_for_error (CloseHandle (self->thread));
416           g_free (self);
417         }
418       win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL));
419     }
420
421   if (g_thread_impl_vtable.CallThisOnThreadExit)
422     g_thread_impl_vtable.CallThisOnThreadExit ();
423
424   _endthreadex (0);
425 }
426
427 static guint __stdcall
428 g_thread_proxy (gpointer data)
429 {
430   GThreadData *self = (GThreadData*) data;
431
432   win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
433
434   self->func (self->data);
435
436   g_thread_exit_win32_impl ();
437
438   g_assert_not_reached ();
439
440   return 0;
441 }
442
443 static void
444 g_thread_create_win32_impl (GThreadFunc func,
445                             gpointer data,
446                             gulong stack_size,
447                             gboolean joinable,
448                             gboolean bound,
449                             GThreadPriority priority,
450                             gpointer thread,
451                             GError **error)
452 {
453   guint ignore;
454   GThreadData *retval;
455
456   g_return_if_fail (func);
457   g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
458   g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
459
460   retval = g_new(GThreadData, 1);
461   retval->func = func;
462   retval->data = data;
463
464   retval->joinable = joinable;
465
466   retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy,
467                                             retval, 0, &ignore);
468
469   if (retval->thread == NULL)
470     {
471       gchar *win_error = g_win32_error_message (GetLastError ());
472       g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
473                    "Error creating thread: %s", win_error);
474       g_free (retval);
475       g_free (win_error);
476       return;
477     }
478
479   *(GThreadData **)thread = retval;
480
481   g_thread_set_priority_win32_impl (thread, priority);
482 }
483
484 static void
485 g_thread_yield_win32_impl (void)
486 {
487   Sleep(0);
488 }
489
490 static void
491 g_thread_join_win32_impl (gpointer thread)
492 {
493   GThreadData *target = *(GThreadData **)thread;
494
495   g_return_if_fail (target->joinable);
496
497   win32_check_for_error (WAIT_FAILED !=
498                          WaitForSingleObject (target->thread, INFINITE));
499
500   win32_check_for_error (CloseHandle (target->thread));
501   g_free (target);
502 }
503
504 /* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */
505
506 static CRITICAL_SECTION g_thread_xp_lock;
507 static DWORD            g_thread_xp_waiter_tls;
508
509 /* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */
510 typedef struct _GThreadXpWaiter GThreadXpWaiter;
511 struct _GThreadXpWaiter
512 {
513   HANDLE                    event;
514   volatile GThreadXpWaiter *next;
515 };
516
517 static GThreadXpWaiter *
518 g_thread_xp_waiter_get (void)
519 {
520   GThreadXpWaiter *waiter;
521
522   waiter = TlsGetValue (g_thread_xp_waiter_tls);
523
524   if G_UNLIKELY (waiter == NULL)
525     {
526       waiter = malloc (sizeof (GThreadXpWaiter));
527       if (waiter == NULL)
528         g_thread_abort (GetLastError (), "malloc");
529       waiter->event = CreateEvent (0, FALSE, FALSE, NULL);
530       if (waiter->event == NULL)
531         g_thread_abort (GetLastError (), "CreateEvent");
532
533       TlsSetValue (g_thread_xp_waiter_tls, waiter);
534     }
535
536   return waiter;
537 }
538
539 static void
540 g_thread_xp_CallThisOnThreadExit (void)
541 {
542   GThreadXpWaiter *waiter;
543
544   waiter = TlsGetValue (g_thread_xp_waiter_tls);
545
546   if (waiter != NULL)
547     {
548       TlsSetValue (g_thread_xp_waiter_tls, NULL);
549       CloseHandle (waiter->event);
550       free (waiter);
551     }
552 }
553
554 /* {{{2 SRWLock emulation */
555 typedef struct
556 {
557   CRITICAL_SECTION critical_section;
558 } GThreadSRWLock;
559
560 static void
561 g_thread_xp_InitializeSRWLock (gpointer mutex)
562 {
563   *(GThreadSRWLock * volatile *) mutex = NULL;
564 }
565
566 static void
567 g_thread_xp_DeleteSRWLock (gpointer mutex)
568 {
569   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
570
571   if (lock)
572     {
573       DeleteCriticalSection (&lock->critical_section);
574       free (lock);
575     }
576 }
577
578 static GThreadSRWLock *
579 g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock)
580 {
581   GThreadSRWLock *result;
582
583   /* It looks like we're missing some barriers here, but this code only
584    * ever runs on Windows XP, which in turn only ever runs on hardware
585    * with a relatively rigid memory model.  The 'volatile' will take
586    * care of the compiler.
587    */
588   result = *lock;
589
590   if G_UNLIKELY (result == NULL)
591     {
592       EnterCriticalSection (&g_thread_xp_lock);
593
594       result = malloc (sizeof (GThreadSRWLock));
595
596       if (result == NULL)
597         g_thread_abort (errno, "malloc");
598
599       InitializeCriticalSection (&result->critical_section);
600       *lock = result;
601
602       LeaveCriticalSection (&g_thread_xp_lock);
603     }
604
605   return result;
606 }
607
608 static void
609 g_thread_xp_AcquireSRWLockExclusive (gpointer mutex)
610 {
611   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
612
613   EnterCriticalSection (&lock->critical_section);
614 }
615
616 static BOOLEAN
617 g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex)
618 {
619   GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex);
620
621   return TryEnterCriticalSection (&lock->critical_section);
622 }
623
624 static void
625 g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex)
626 {
627   GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex;
628
629   /* We need this until we fix some weird parts of GLib that try to
630    * unlock freshly-allocated mutexes.
631    */
632   if (lock != NULL)
633     LeaveCriticalSection (&lock->critical_section);
634 }
635
636 /* {{{2 CONDITION_VARIABLE emulation */
637 typedef struct
638 {
639   volatile GThreadXpWaiter  *first;
640   volatile GThreadXpWaiter **last_ptr;
641 } GThreadXpCONDITION_VARIABLE;
642
643 static void
644 g_thread_xp_InitializeConditionVariable (gpointer cond)
645 {
646   *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL;
647 }
648
649 static void
650 g_thread_xp_DeleteConditionVariable (gpointer cond)
651 {
652   GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond;
653
654   if (cv)
655     free (cv);
656 }
657
658 static GThreadXpCONDITION_VARIABLE *
659 g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond)
660 {
661   GThreadXpCONDITION_VARIABLE *result;
662
663   /* It looks like we're missing some barriers here, but this code only
664    * ever runs on Windows XP, which in turn only ever runs on hardware
665    * with a relatively rigid memory model.  The 'volatile' will take
666    * care of the compiler.
667    */
668   result = *cond;
669
670   if G_UNLIKELY (result == NULL)
671     {
672       result = malloc (sizeof (GThreadXpCONDITION_VARIABLE));
673
674       if (result == NULL)
675         g_thread_abort (errno, "malloc");
676
677       result->first = NULL;
678       result->last_ptr = &result->first;
679
680       if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL)
681         {
682           free (result);
683           result = *cond;
684         }
685     }
686
687   return result;
688 }
689
690 static BOOL
691 g_thread_xp_SleepConditionVariableSRW (gpointer cond,
692                                        gpointer mutex,
693                                        DWORD    timeout,
694                                        ULONG    flags)
695 {
696   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
697   GThreadXpWaiter *waiter = g_thread_xp_waiter_get ();
698   DWORD status;
699
700   waiter->next = NULL;
701
702   EnterCriticalSection (&g_thread_xp_lock);
703   *cv->last_ptr = waiter;
704   cv->last_ptr = &waiter->next;
705   LeaveCriticalSection (&g_thread_xp_lock);
706
707   g_mutex_unlock (mutex);
708   status = WaitForSingleObject (waiter->event, timeout);
709
710   if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0)
711     g_thread_abort (GetLastError (), "WaitForSingleObject");
712
713   g_mutex_lock (mutex);
714
715   return status == WAIT_OBJECT_0;
716 }
717
718 static void
719 g_thread_xp_WakeConditionVariable (gpointer cond)
720 {
721   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
722   volatile GThreadXpWaiter *waiter;
723
724   EnterCriticalSection (&g_thread_xp_lock);
725   waiter = cv->first;
726   if (waiter != NULL)
727     {
728       cv->first = waiter->next;
729       if (cv->first == NULL)
730         cv->last_ptr = &cv->first;
731     }
732   LeaveCriticalSection (&g_thread_xp_lock);
733
734   if (waiter != NULL)
735     SetEvent (waiter->event);
736 }
737
738 static void
739 g_thread_xp_WakeAllConditionVariable (gpointer cond)
740 {
741   GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond);
742   volatile GThreadXpWaiter *waiter;
743
744   EnterCriticalSection (&g_thread_xp_lock);
745   waiter = cv->first;
746   cv->first = NULL;
747   cv->last_ptr = &cv->first;
748   LeaveCriticalSection (&g_thread_xp_lock);
749
750   while (waiter != NULL)
751     {
752       volatile GThreadXpWaiter *next;
753
754       next = waiter->next;
755       SetEvent (waiter->event);
756       waiter = next;
757     }
758 }
759
760 /* {{{2 XP Setup */
761 static void
762 g_thread_xp_init (void)
763 {
764   static const GThreadImplVtable g_thread_xp_impl_vtable = {
765     g_thread_xp_CallThisOnThreadExit,
766     g_thread_xp_InitializeSRWLock,
767     g_thread_xp_DeleteSRWLock,
768     g_thread_xp_AcquireSRWLockExclusive,
769     g_thread_xp_TryAcquireSRWLockExclusive,
770     g_thread_xp_ReleaseSRWLockExclusive,
771     g_thread_xp_InitializeConditionVariable,
772     g_thread_xp_DeleteConditionVariable,
773     g_thread_xp_SleepConditionVariableSRW,
774     g_thread_xp_WakeAllConditionVariable,
775     g_thread_xp_WakeConditionVariable
776   };
777
778   InitializeCriticalSection (&g_thread_xp_lock);
779   g_thread_xp_waiter_tls = TlsAlloc ();
780
781   g_thread_impl_vtable = g_thread_xp_impl_vtable;
782 }
783
784 /* {{{1 Epilogue */
785
786 GThreadFunctions g_thread_functions_for_glib_use =
787 {
788   g_mutex_new,           /* mutex */
789   g_mutex_lock,
790   g_mutex_trylock,
791   g_mutex_unlock,
792   g_mutex_free,
793   g_cond_new,            /* condition */
794   g_cond_signal,
795   g_cond_broadcast,
796   g_cond_wait,
797   g_cond_timed_wait,
798   g_cond_free,
799   g_private_new,         /* private thread data */
800   g_private_get,
801   g_private_set,
802   g_thread_create_win32_impl,       /* thread */
803   g_thread_yield_win32_impl,
804   g_thread_join_win32_impl,
805   g_thread_exit_win32_impl,
806   g_thread_set_priority_win32_impl,
807   g_thread_self_win32_impl,
808   NULL                             /* no equal function necessary */
809 };
810
811 void
812 _g_thread_impl_init (void)
813 {
814   static gboolean beenhere = FALSE;
815
816   if (beenhere)
817     return;
818
819   beenhere = TRUE;
820
821   printf ("thread init\n");
822   win32_check_for_error (TLS_OUT_OF_INDEXES !=
823                          (g_thread_self_tls = TlsAlloc ()));
824   win32_check_for_error (TLS_OUT_OF_INDEXES !=
825                          (g_private_tls = TlsAlloc ()));
826 }
827
828 static gboolean
829 g_thread_lookup_native_funcs (void)
830 {
831   GThreadImplVtable native_vtable = { 0, };
832   HMODULE kernel32;
833
834   kernel32 = GetModuleHandle ("KERNEL32.DLL");
835
836   if (kernel32 == NULL)
837     return FALSE;
838
839 #define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE
840   GET_FUNC(InitializeSRWLock);
841   GET_FUNC(AcquireSRWLockExclusive);
842   GET_FUNC(TryAcquireSRWLockExclusive);
843   GET_FUNC(ReleaseSRWLockExclusive);
844
845   GET_FUNC(InitializeConditionVariable);
846   GET_FUNC(SleepConditionVariableSRW);
847   GET_FUNC(WakeAllConditionVariable);
848   GET_FUNC(WakeConditionVariable);
849 #undef GET_FUNC
850
851   g_thread_impl_vtable = native_vtable;
852
853   return TRUE;
854 }
855
856 G_GNUC_INTERNAL void
857 g_thread_DllMain (void)
858 {
859   /* XXX This is broken right now for some unknown reason...
860
861   if (g_thread_lookup_native_funcs ())
862     fprintf (stderr, "(debug) GThread using native mode\n");
863   else
864 */
865     {
866       fprintf (stderr, "(debug) GThread using Windows XP mode\n");
867       g_thread_xp_init ();
868     }
869 }
870
871 /* vim:set foldmethod=marker: */
872