diff options
Diffstat (limited to 'glib/glib/gthread-win32.c')
-rw-r--r-- | glib/glib/gthread-win32.c | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/glib/glib/gthread-win32.c b/glib/glib/gthread-win32.c new file mode 100644 index 0000000..5a9ac97 --- /dev/null +++ b/glib/glib/gthread-win32.c @@ -0,0 +1,1032 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: solaris thread system implementation + * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe + * Copyright 2001 Hans Breuer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* The GMutex and GCond implementations in this file are some of the + * lowest-level code in GLib. All other parts of GLib (messages, + * memory, slices, etc) assume that they can freely use these facilities + * without risking recursion. + * + * As such, these functions are NOT permitted to call any other part of + * GLib. + * + * The thread manipulation functions (create, exit, join, etc.) have + * more freedom -- they can do as they please. + */ + +#include "config.h" + +#include "glib.h" +#include "gthread.h" +#include "gthreadprivate.h" +#include "gslice.h" + +#include <windows.h> + +#include <process.h> +#include <stdlib.h> +#include <stdio.h> + +static void +g_thread_abort (gint status, + const gchar *function) +{ + fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s. Aborting.\n", + strerror (status), function); + abort (); +} + +/* Starting with Vista and Windows 2008, we have access to the + * CONDITION_VARIABLE and SRWLock primatives on Windows, which are + * pretty reasonable approximations of the primatives specified in + * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively). + * + * Both of these types are structs containing a single pointer. That + * pointer is used as an atomic bitfield to support user-space mutexes + * that only get the kernel involved in cases of contention (similar + * to how futex()-based mutexes work on Linux). The biggest advantage + * of these new types is that they can be statically initialised to + * zero. That means that they are completely ABI compatible with our + * GMutex and GCond APIs. + * + * Unfortunately, Windows XP lacks these facilities and GLib still + * needs to support Windows XP. Our approach here is as follows: + * + * - avoid depending on structure declarations at compile-time by + * declaring our own GMutex and GCond strutures to be + * ABI-compatible with SRWLock and CONDITION_VARIABLE and using + * those instead + * + * - avoid a hard dependency on the symbols used to manipulate these + * structures by doing a dynamic lookup of those symbols at + * runtime + * + * - if the symbols are not available, emulate them using other + * primatives + * + * Using this approach also allows us to easily build a GLib that lacks + * support for Windows XP or to remove this code entirely when XP is no + * longer supported (end of line is currently April 8, 2014). + */ +typedef struct +{ + void (__stdcall * CallThisOnThreadExit) (void); /* fake */ + + void (__stdcall * InitializeSRWLock) (gpointer lock); + void (__stdcall * DeleteSRWLock) (gpointer lock); /* fake */ + void (__stdcall * AcquireSRWLockExclusive) (gpointer lock); + BOOLEAN (__stdcall * TryAcquireSRWLockExclusive) (gpointer lock); + void (__stdcall * ReleaseSRWLockExclusive) (gpointer lock); + void (__stdcall * AcquireSRWLockShared) (gpointer lock); + BOOLEAN (__stdcall * TryAcquireSRWLockShared) (gpointer lock); + void (__stdcall * ReleaseSRWLockShared) (gpointer lock); + + void (__stdcall * InitializeConditionVariable) (gpointer cond); + void (__stdcall * DeleteConditionVariable) (gpointer cond); /* fake */ + BOOL (__stdcall * SleepConditionVariableSRW) (gpointer cond, + gpointer lock, + DWORD timeout, + ULONG flags); + void (__stdcall * WakeAllConditionVariable) (gpointer cond); + void (__stdcall * WakeConditionVariable) (gpointer cond); +} GThreadImplVtable; + +static GThreadImplVtable g_thread_impl_vtable; + +/* {{{1 GMutex */ +void +g_mutex_init (GMutex *mutex) +{ + g_thread_impl_vtable.InitializeSRWLock (mutex); +} + +void +g_mutex_clear (GMutex *mutex) +{ + if (g_thread_impl_vtable.DeleteSRWLock != NULL) + g_thread_impl_vtable.DeleteSRWLock (mutex); +} + +void +g_mutex_lock (GMutex *mutex) +{ + g_thread_impl_vtable.AcquireSRWLockExclusive (mutex); +} + +gboolean +g_mutex_trylock (GMutex *mutex) +{ + return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex); +} + +void +g_mutex_unlock (GMutex *mutex) +{ + g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex); +} + +/* {{{1 GRecMutex */ + +static CRITICAL_SECTION * +g_rec_mutex_impl_new (void) +{ + CRITICAL_SECTION *cs; + + cs = g_slice_new (CRITICAL_SECTION); + InitializeCriticalSection (cs); + + return cs; +} + +static void +g_rec_mutex_impl_free (CRITICAL_SECTION *cs) +{ + DeleteCriticalSection (cs); + g_slice_free (CRITICAL_SECTION, cs); +} + +static CRITICAL_SECTION * +g_rec_mutex_get_impl (GRecMutex *mutex) +{ + CRITICAL_SECTION *impl = mutex->p; + + if G_UNLIKELY (mutex->p == NULL) + { + impl = g_rec_mutex_impl_new (); + if (InterlockedCompareExchangePointer (&mutex->p, impl, NULL) != NULL) + g_rec_mutex_impl_free (impl); + impl = mutex->p; + } + + return impl; +} + +void +g_rec_mutex_init (GRecMutex *mutex) +{ + mutex->p = g_rec_mutex_impl_new (); +} + +void +g_rec_mutex_clear (GRecMutex *mutex) +{ + g_rec_mutex_impl_free (mutex->p); +} + +void +g_rec_mutex_lock (GRecMutex *mutex) +{ + EnterCriticalSection (g_rec_mutex_get_impl (mutex)); +} + +void +g_rec_mutex_unlock (GRecMutex *mutex) +{ + LeaveCriticalSection (mutex->p); +} + +gboolean +g_rec_mutex_trylock (GRecMutex *mutex) +{ + return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex)); +} + +/* {{{1 GRWLock */ + +void +g_rw_lock_init (GRWLock *lock) +{ + g_thread_impl_vtable.InitializeSRWLock (lock); +} + +void +g_rw_lock_clear (GRWLock *lock) +{ + if (g_thread_impl_vtable.DeleteSRWLock != NULL) + g_thread_impl_vtable.DeleteSRWLock (lock); +} + +void +g_rw_lock_writer_lock (GRWLock *lock) +{ + g_thread_impl_vtable.AcquireSRWLockExclusive (lock); +} + +gboolean +g_rw_lock_writer_trylock (GRWLock *lock) +{ + return g_thread_impl_vtable.TryAcquireSRWLockExclusive (lock); +} + +void +g_rw_lock_writer_unlock (GRWLock *lock) +{ + g_thread_impl_vtable.ReleaseSRWLockExclusive (lock); +} + +void +g_rw_lock_reader_lock (GRWLock *lock) +{ + g_thread_impl_vtable.AcquireSRWLockShared (lock); +} + +gboolean +g_rw_lock_reader_trylock (GRWLock *lock) +{ + return g_thread_impl_vtable.TryAcquireSRWLockShared (lock); +} + +void +g_rw_lock_reader_unlock (GRWLock *lock) +{ + g_thread_impl_vtable.ReleaseSRWLockShared (lock); +} + +/* {{{1 GCond */ +void +g_cond_init (GCond *cond) +{ + g_thread_impl_vtable.InitializeConditionVariable (cond); +} + +void +g_cond_clear (GCond *cond) +{ + if (g_thread_impl_vtable.DeleteConditionVariable) + g_thread_impl_vtable.DeleteConditionVariable (cond); +} + +void +g_cond_signal (GCond *cond) +{ + g_thread_impl_vtable.WakeConditionVariable (cond); +} + +void +g_cond_broadcast (GCond *cond) +{ + g_thread_impl_vtable.WakeAllConditionVariable (cond); +} + +void +g_cond_wait (GCond *cond, + GMutex *entered_mutex) +{ + g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0); +} + +gboolean +g_cond_wait_until (GCond *cond, + GMutex *entered_mutex, + gint64 end_time) +{ + gint64 span; + + span = end_time - g_get_monotonic_time (); + + if G_UNLIKELY (span < 0) + span = 0; + + if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32) + span = INFINITE; + + return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0); +} + +/* {{{1 GPrivate */ + +typedef struct _GPrivateDestructor GPrivateDestructor; + +struct _GPrivateDestructor +{ + DWORD index; + GDestroyNotify notify; + GPrivateDestructor *next; +}; + +static GPrivateDestructor * volatile g_private_destructors; +static CRITICAL_SECTION g_private_lock; + +static DWORD +g_private_get_impl (GPrivate *key) +{ + DWORD impl = (DWORD) key->p; + + if G_UNLIKELY (impl == 0) + { + EnterCriticalSection (&g_private_lock); + impl = (DWORD) key->p; + if (impl == 0) + { + GPrivateDestructor *destructor; + + impl = TlsAlloc (); + + if (impl == TLS_OUT_OF_INDEXES) + g_thread_abort (0, "TlsAlloc"); + + if (key->notify != NULL) + { + destructor = malloc (sizeof (GPrivateDestructor)); + if G_UNLIKELY (destructor == NULL) + g_thread_abort (errno, "malloc"); + destructor->index = impl; + destructor->notify = key->notify; + destructor->next = g_private_destructors; + + /* We need to do an atomic store due to the unlocked + * access to the destructor list from the thread exit + * function. + * + * It can double as a sanity check... + */ + if (InterlockedCompareExchangePointer (&g_private_destructors, destructor, + destructor->next) != destructor->next) + g_thread_abort (0, "g_private_get_impl(1)"); + } + + /* Ditto, due to the unlocked access on the fast path */ + if (InterlockedCompareExchangePointer (&key->p, impl, NULL) != NULL) + g_thread_abort (0, "g_private_get_impl(2)"); + } + LeaveCriticalSection (&g_private_lock); + } + + return impl; +} + +gpointer +g_private_get (GPrivate *key) +{ + return TlsGetValue (g_private_get_impl (key)); +} + +void +g_private_set (GPrivate *key, + gpointer value) +{ + TlsSetValue (g_private_get_impl (key), value); +} + +void +g_private_replace (GPrivate *key, + gpointer value) +{ + DWORD impl = g_private_get_impl (key); + gpointer old; + + old = TlsGetValue (impl); + if (old && key->notify) + key->notify (old); + TlsSetValue (impl, value); +} + +/* {{{1 GThread */ + +#define win32_check_for_error(what) G_STMT_START{ \ + if (!(what)) \ + g_error ("file %s: line %d (%s): error %s during %s", \ + __FILE__, __LINE__, G_STRFUNC, \ + g_win32_error_message (GetLastError ()), #what); \ + }G_STMT_END + +#define G_MUTEX_SIZE (sizeof (gpointer)) + +typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); + +typedef struct +{ + GRealThread thread; + + GThreadFunc proxy; + HANDLE handle; +} GThreadWin32; + +void +g_system_thread_free (GRealThread *thread) +{ + GThreadWin32 *wt = (GThreadWin32 *) thread; + + win32_check_for_error (CloseHandle (wt->handle)); + g_slice_free (GThreadWin32, wt); +} + +void +g_system_thread_exit (void) +{ + _endthreadex (0); +} + +static guint __stdcall +g_thread_win32_proxy (gpointer data) +{ + GThreadWin32 *self = data; + + self->proxy (self); + + g_system_thread_exit (); + + g_assert_not_reached (); + + return 0; +} + +GRealThread * +g_system_thread_new (GThreadFunc func, + gulong stack_size, + GError **error) +{ + GThreadWin32 *thread; + guint ignore; + + thread = g_slice_new0 (GThreadWin32); + thread->proxy = func; + + thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread, 0, &ignore); + + if (thread->handle == NULL) + { + gchar *win_error = g_win32_error_message (GetLastError ()); + g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, + "Error creating thread: %s", win_error); + g_free (win_error); + g_slice_free (GThreadWin32, thread); + return NULL; + } + + return (GRealThread *) thread; +} + +void +g_thread_yield (void) +{ + Sleep(0); +} + +void +g_system_thread_wait (GRealThread *thread) +{ + GThreadWin32 *wt = (GThreadWin32 *) thread; + + win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE)); +} + +void +g_system_thread_set_name (const gchar *name) +{ + /* FIXME: implement */ +} + +/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */ + +static CRITICAL_SECTION g_thread_xp_lock; +static DWORD g_thread_xp_waiter_tls; + +/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */ +typedef struct _GThreadXpWaiter GThreadXpWaiter; +struct _GThreadXpWaiter +{ + HANDLE event; + volatile GThreadXpWaiter *next; + volatile GThreadXpWaiter **my_owner; +}; + +static GThreadXpWaiter * +g_thread_xp_waiter_get (void) +{ + GThreadXpWaiter *waiter; + + waiter = TlsGetValue (g_thread_xp_waiter_tls); + + if G_UNLIKELY (waiter == NULL) + { + waiter = malloc (sizeof (GThreadXpWaiter)); + if (waiter == NULL) + g_thread_abort (GetLastError (), "malloc"); + waiter->event = CreateEvent (0, FALSE, FALSE, NULL); + if (waiter->event == NULL) + g_thread_abort (GetLastError (), "CreateEvent"); + waiter->my_owner = NULL; + + TlsSetValue (g_thread_xp_waiter_tls, waiter); + } + + return waiter; +} + +static void __stdcall +g_thread_xp_CallThisOnThreadExit (void) +{ + GThreadXpWaiter *waiter; + + waiter = TlsGetValue (g_thread_xp_waiter_tls); + + if (waiter != NULL) + { + TlsSetValue (g_thread_xp_waiter_tls, NULL); + CloseHandle (waiter->event); + free (waiter); + } +} + +/* {{{2 SRWLock emulation */ +typedef struct +{ + CRITICAL_SECTION writer_lock; + gboolean ever_shared; /* protected by writer_lock */ + gboolean writer_locked; /* protected by writer_lock */ + + /* below is only ever touched if ever_shared becomes true */ + CRITICAL_SECTION atomicity; + GThreadXpWaiter *queued_writer; /* protected by atomicity lock */ + gint num_readers; /* protected by atomicity lock */ +} GThreadSRWLock; + +static void __stdcall +g_thread_xp_InitializeSRWLock (gpointer mutex) +{ + *(GThreadSRWLock * volatile *) mutex = NULL; +} + +static void __stdcall +g_thread_xp_DeleteSRWLock (gpointer mutex) +{ + GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; + + if (lock) + { + if (lock->ever_shared) + DeleteCriticalSection (&lock->atomicity); + + DeleteCriticalSection (&lock->writer_lock); + free (lock); + } +} + +static GThreadSRWLock * __stdcall +g_thread_xp_get_srwlock (GThreadSRWLock * volatile *lock) +{ + GThreadSRWLock *result; + + /* It looks like we're missing some barriers here, but this code only + * ever runs on Windows XP, which in turn only ever runs on hardware + * with a relatively rigid memory model. The 'volatile' will take + * care of the compiler. + */ + result = *lock; + + if G_UNLIKELY (result == NULL) + { + EnterCriticalSection (&g_thread_xp_lock); + + /* Check again */ + result = *lock; + if (result == NULL) + { + result = malloc (sizeof (GThreadSRWLock)); + + if (result == NULL) + g_thread_abort (errno, "malloc"); + + InitializeCriticalSection (&result->writer_lock); + result->writer_locked = FALSE; + result->ever_shared = FALSE; + *lock = result; + } + + LeaveCriticalSection (&g_thread_xp_lock); + } + + return result; +} + +static void __stdcall +g_thread_xp_AcquireSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + EnterCriticalSection (&lock->writer_lock); + + /* CRITICAL_SECTION is reentrant, but SRWLock is not. + * Detect the deadlock that would occur on later Windows version. + */ + g_assert (!lock->writer_locked); + lock->writer_locked = TRUE; + + if (lock->ever_shared) + { + GThreadXpWaiter *waiter = NULL; + + EnterCriticalSection (&lock->atomicity); + if (lock->num_readers > 0) + lock->queued_writer = waiter = g_thread_xp_waiter_get (); + LeaveCriticalSection (&lock->atomicity); + + if (waiter != NULL) + WaitForSingleObject (waiter->event, INFINITE); + + lock->queued_writer = NULL; + } +} + +static BOOLEAN __stdcall +g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + if (!TryEnterCriticalSection (&lock->writer_lock)) + return FALSE; + + /* CRITICAL_SECTION is reentrant, but SRWLock is not. + * Ensure that this properly returns FALSE (as SRWLock would). + */ + if G_UNLIKELY (lock->writer_locked) + { + LeaveCriticalSection (&lock->writer_lock); + return FALSE; + } + + lock->writer_locked = TRUE; + + if (lock->ever_shared) + { + gboolean available; + + EnterCriticalSection (&lock->atomicity); + available = lock->num_readers == 0; + LeaveCriticalSection (&lock->atomicity); + + if (!available) + { + LeaveCriticalSection (&lock->writer_lock); + return FALSE; + } + } + + return TRUE; +} + +static void __stdcall +g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; + + lock->writer_locked = FALSE; + + /* We need this until we fix some weird parts of GLib that try to + * unlock freshly-allocated mutexes. + */ + if (lock != NULL) + LeaveCriticalSection (&lock->writer_lock); +} + +static void +g_thread_xp_srwlock_become_reader (GThreadSRWLock *lock) +{ + if G_UNLIKELY (!lock->ever_shared) + { + InitializeCriticalSection (&lock->atomicity); + lock->queued_writer = NULL; + lock->num_readers = 0; + + lock->ever_shared = TRUE; + } + + EnterCriticalSection (&lock->atomicity); + lock->num_readers++; + LeaveCriticalSection (&lock->atomicity); +} + +static void __stdcall +g_thread_xp_AcquireSRWLockShared (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + EnterCriticalSection (&lock->writer_lock); + + /* See g_thread_xp_AcquireSRWLockExclusive */ + g_assert (!lock->writer_locked); + + g_thread_xp_srwlock_become_reader (lock); + + LeaveCriticalSection (&lock->writer_lock); +} + +static BOOLEAN __stdcall +g_thread_xp_TryAcquireSRWLockShared (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + if (!TryEnterCriticalSection (&lock->writer_lock)) + return FALSE; + + /* See g_thread_xp_AcquireSRWLockExclusive */ + if G_UNLIKELY (lock->writer_locked) + { + LeaveCriticalSection (&lock->writer_lock); + return FALSE; + } + + g_thread_xp_srwlock_become_reader (lock); + + LeaveCriticalSection (&lock->writer_lock); + + return TRUE; +} + +static void __stdcall +g_thread_xp_ReleaseSRWLockShared (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + EnterCriticalSection (&lock->atomicity); + + lock->num_readers--; + + if (lock->num_readers == 0 && lock->queued_writer) + SetEvent (lock->queued_writer->event); + + LeaveCriticalSection (&lock->atomicity); +} + +/* {{{2 CONDITION_VARIABLE emulation */ +typedef struct +{ + volatile GThreadXpWaiter *first; + volatile GThreadXpWaiter **last_ptr; +} GThreadXpCONDITION_VARIABLE; + +static void __stdcall +g_thread_xp_InitializeConditionVariable (gpointer cond) +{ + *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL; +} + +static void __stdcall +g_thread_xp_DeleteConditionVariable (gpointer cond) +{ + GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond; + + if (cv) + free (cv); +} + +static GThreadXpCONDITION_VARIABLE * __stdcall +g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond) +{ + GThreadXpCONDITION_VARIABLE *result; + + /* It looks like we're missing some barriers here, but this code only + * ever runs on Windows XP, which in turn only ever runs on hardware + * with a relatively rigid memory model. The 'volatile' will take + * care of the compiler. + */ + result = *cond; + + if G_UNLIKELY (result == NULL) + { + result = malloc (sizeof (GThreadXpCONDITION_VARIABLE)); + + if (result == NULL) + g_thread_abort (errno, "malloc"); + + result->first = NULL; + result->last_ptr = &result->first; + + if (InterlockedCompareExchangePointer (cond, result, NULL) != NULL) + { + free (result); + result = *cond; + } + } + + return result; +} + +static BOOL __stdcall +g_thread_xp_SleepConditionVariableSRW (gpointer cond, + gpointer mutex, + DWORD timeout, + ULONG flags) +{ + GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); + GThreadXpWaiter *waiter = g_thread_xp_waiter_get (); + DWORD status; + + waiter->next = NULL; + + EnterCriticalSection (&g_thread_xp_lock); + waiter->my_owner = cv->last_ptr; + *cv->last_ptr = waiter; + cv->last_ptr = &waiter->next; + LeaveCriticalSection (&g_thread_xp_lock); + + g_mutex_unlock (mutex); + status = WaitForSingleObject (waiter->event, timeout); + + if (status != WAIT_TIMEOUT && status != WAIT_OBJECT_0) + g_thread_abort (GetLastError (), "WaitForSingleObject"); + g_mutex_lock (mutex); + + if (status == WAIT_TIMEOUT) + { + EnterCriticalSection (&g_thread_xp_lock); + if (waiter->my_owner) + { + if (waiter->next) + waiter->next->my_owner = waiter->my_owner; + else + cv->last_ptr = waiter->my_owner; + *waiter->my_owner = waiter->next; + waiter->my_owner = NULL; + } + LeaveCriticalSection (&g_thread_xp_lock); + } + + return status == WAIT_OBJECT_0; +} + +static void __stdcall +g_thread_xp_WakeConditionVariable (gpointer cond) +{ + GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); + volatile GThreadXpWaiter *waiter; + + EnterCriticalSection (&g_thread_xp_lock); + + waiter = cv->first; + if (waiter != NULL) + { + waiter->my_owner = NULL; + cv->first = waiter->next; + if (cv->first != NULL) + cv->first->my_owner = &cv->first; + else + cv->last_ptr = &cv->first; + } + + if (waiter != NULL) + SetEvent (waiter->event); + + LeaveCriticalSection (&g_thread_xp_lock); +} + +static void __stdcall +g_thread_xp_WakeAllConditionVariable (gpointer cond) +{ + GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); + volatile GThreadXpWaiter *waiter; + + EnterCriticalSection (&g_thread_xp_lock); + + waiter = cv->first; + cv->first = NULL; + cv->last_ptr = &cv->first; + + while (waiter != NULL) + { + volatile GThreadXpWaiter *next; + + next = waiter->next; + SetEvent (waiter->event); + waiter->my_owner = NULL; + waiter = next; + } + + LeaveCriticalSection (&g_thread_xp_lock); +} + +/* {{{2 XP Setup */ +static void +g_thread_xp_init (void) +{ + static const GThreadImplVtable g_thread_xp_impl_vtable = { + g_thread_xp_CallThisOnThreadExit, + g_thread_xp_InitializeSRWLock, + g_thread_xp_DeleteSRWLock, + g_thread_xp_AcquireSRWLockExclusive, + g_thread_xp_TryAcquireSRWLockExclusive, + g_thread_xp_ReleaseSRWLockExclusive, + g_thread_xp_AcquireSRWLockShared, + g_thread_xp_TryAcquireSRWLockShared, + g_thread_xp_ReleaseSRWLockShared, + g_thread_xp_InitializeConditionVariable, + g_thread_xp_DeleteConditionVariable, + g_thread_xp_SleepConditionVariableSRW, + g_thread_xp_WakeAllConditionVariable, + g_thread_xp_WakeConditionVariable + }; + + InitializeCriticalSection (&g_thread_xp_lock); + g_thread_xp_waiter_tls = TlsAlloc (); + + g_thread_impl_vtable = g_thread_xp_impl_vtable; +} + +/* {{{1 Epilogue */ + +static gboolean +g_thread_lookup_native_funcs (void) +{ + GThreadImplVtable native_vtable = { 0, }; + HMODULE kernel32; + + kernel32 = GetModuleHandle ("KERNEL32.DLL"); + + if (kernel32 == NULL) + return FALSE; + +#define GET_FUNC(name) if ((native_vtable.name = (void *) GetProcAddress (kernel32, #name)) == NULL) return FALSE + GET_FUNC(InitializeSRWLock); + GET_FUNC(AcquireSRWLockExclusive); + GET_FUNC(TryAcquireSRWLockExclusive); + GET_FUNC(ReleaseSRWLockExclusive); + GET_FUNC(AcquireSRWLockShared); + GET_FUNC(TryAcquireSRWLockShared); + GET_FUNC(ReleaseSRWLockShared); + + GET_FUNC(InitializeConditionVariable); + GET_FUNC(SleepConditionVariableSRW); + GET_FUNC(WakeAllConditionVariable); + GET_FUNC(WakeConditionVariable); +#undef GET_FUNC + + g_thread_impl_vtable = native_vtable; + + return TRUE; +} + +G_GNUC_INTERNAL void +g_thread_win32_init (void) +{ + if (!g_thread_lookup_native_funcs ()) + g_thread_xp_init (); + + InitializeCriticalSection (&g_private_lock); +} + +G_GNUC_INTERNAL void +g_thread_win32_thread_detach (void) +{ + gboolean dtors_called; + + do + { + GPrivateDestructor *dtor; + + /* We go by the POSIX book on this one. + * + * If we call a destructor then there is a chance that some new + * TLS variables got set by code called in that destructor. + * + * Loop until nothing is left. + */ + dtors_called = FALSE; + + for (dtor = g_private_destructors; dtor; dtor = dtor->next) + { + gpointer value; + + value = TlsGetValue (dtor->index); + if (value != NULL && dtor->notify != NULL) + { + /* POSIX says to clear this before the call */ + TlsSetValue (dtor->index, NULL); + dtor->notify (value); + dtors_called = TRUE; + } + } + } + while (dtors_called); + + if (g_thread_impl_vtable.CallThisOnThreadExit) + g_thread_impl_vtable.CallThisOnThreadExit (); +} + +/* vim:set foldmethod=marker: */ |