/* 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 #include #include #include 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: */