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