diff options
Diffstat (limited to 'glib/gthread-win32.c')
-rw-r--r-- | glib/gthread-win32.c | 564 |
1 files changed, 16 insertions, 548 deletions
diff --git a/glib/gthread-win32.c b/glib/gthread-win32.c index a81f00a5c..1ad5ece80 100644 --- a/glib/gthread-win32.c +++ b/glib/gthread-win32.c @@ -73,81 +73,36 @@ g_thread_abort (gint status, * 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); + InitializeSRWLock ((gpointer) 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); + AcquireSRWLockExclusive ((gpointer) mutex); } gboolean g_mutex_trylock (GMutex *mutex) { - return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex); + return TryAcquireSRWLockExclusive ((gpointer) mutex); } void g_mutex_unlock (GMutex *mutex) { - g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex); + ReleaseSRWLockExclusive ((gpointer) mutex); } /* {{{1 GRecMutex */ @@ -221,83 +176,79 @@ g_rec_mutex_trylock (GRecMutex *mutex) void g_rw_lock_init (GRWLock *lock) { - g_thread_impl_vtable.InitializeSRWLock (lock); + InitializeSRWLock ((gpointer) 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); + AcquireSRWLockExclusive ((gpointer) lock); } gboolean g_rw_lock_writer_trylock (GRWLock *lock) { - return g_thread_impl_vtable.TryAcquireSRWLockExclusive (lock); + return TryAcquireSRWLockExclusive ((gpointer) lock); } void g_rw_lock_writer_unlock (GRWLock *lock) { - g_thread_impl_vtable.ReleaseSRWLockExclusive (lock); + ReleaseSRWLockExclusive ((gpointer) lock); } void g_rw_lock_reader_lock (GRWLock *lock) { - g_thread_impl_vtable.AcquireSRWLockShared (lock); + AcquireSRWLockShared ((gpointer) lock); } gboolean g_rw_lock_reader_trylock (GRWLock *lock) { - return g_thread_impl_vtable.TryAcquireSRWLockShared (lock); + return TryAcquireSRWLockShared ((gpointer) lock); } void g_rw_lock_reader_unlock (GRWLock *lock) { - g_thread_impl_vtable.ReleaseSRWLockShared (lock); + ReleaseSRWLockShared ((gpointer) lock); } /* {{{1 GCond */ void g_cond_init (GCond *cond) { - g_thread_impl_vtable.InitializeConditionVariable (cond); + InitializeConditionVariable ((gpointer) 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); + WakeConditionVariable ((gpointer) cond); } void g_cond_broadcast (GCond *cond) { - g_thread_impl_vtable.WakeAllConditionVariable (cond); + WakeAllConditionVariable ((gpointer) cond); } void g_cond_wait (GCond *cond, GMutex *entered_mutex) { - g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0); + SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, INFINITE, 0); } gboolean @@ -326,7 +277,7 @@ g_cond_wait_until (GCond *cond, if (span_millis >= INFINITE) span_millis = INFINITE - 1; - signalled = g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span_millis, 0); + signalled = SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, span_millis, 0); if (signalled) break; @@ -580,491 +531,11 @@ g_system_thread_set_name (const gchar *name) SetThreadName ((DWORD) -1, name); } -/* {{{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); #ifndef _MSC_VER @@ -1109,9 +580,6 @@ g_thread_win32_thread_detach (void) } } while (dtors_called); - - if (g_thread_impl_vtable.CallThisOnThreadExit) - g_thread_impl_vtable.CallThisOnThreadExit (); } void |