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