| /* 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 "glib-init.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; |
| } |
| |
| void |
| g_thread_win32_init (void) |
| { |
| if (!g_thread_lookup_native_funcs ()) |
| g_thread_xp_init (); |
| |
| InitializeCriticalSection (&g_private_lock); |
| } |
| |
| 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: */ |