X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=glib%2Fgthread-win32.c;h=58e244ebedaad5e795089117f9fdaec596fdbe40;hb=073339230bc0c4b42387f786c6b0d7aaca3f1a81;hp=91510c5e300fee367ee109c5b176beff0a7754f6;hpb=22b3e26034017f19c81a3f2bfa7e9c8ec5a690a8;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gthread-win32.c b/glib/gthread-win32.c index 91510c5..58e244e 100644 --- a/glib/gthread-win32.c +++ b/glib/gthread-win32.c @@ -5,10 +5,12 @@ * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe * Copyright 2001 Hans Breuer * + * SPDX-License-Identifier: LGPL-2.1-or-later + * * 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. + * version 2.1 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 @@ -16,9 +18,7 @@ * 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. + * License along with this library; if not, see . */ /* @@ -42,9 +42,12 @@ #include "config.h" +#include "glib.h" +#include "glib-init.h" #include "gthread.h" +#include "gthreadprivate.h" +#include "gslice.h" -#define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */ #include #include @@ -57,12 +60,12 @@ g_thread_abort (gint status, { fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s. Aborting.\n", strerror (status), function); - abort (); + g_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 + * CONDITION_VARIABLE and SRWLock primitives on Windows, which are + * pretty reasonable approximations of the primitives specified in * POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively). * * Both of these types are structs containing a single pointer. That @@ -70,857 +73,667 @@ g_thread_abort (gint status, * 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. This allows us to use them directly and still support: - * - * GMutex mutex = G_MUTEX_INIT; - * - * and - * - * GCond cond = G_COND_INIT; - * - * 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). + * zero. That means that they are completely ABI compatible with our + * GMutex and GCond APIs. */ -typedef struct -{ - void (* CallThisOnThreadExit) (void); /* fake */ - - void (* InitializeSRWLock) (gpointer lock); - void (* DeleteSRWLock) (gpointer lock); /* fake */ - void (* AcquireSRWLockExclusive) (gpointer lock); - BOOLEAN (* TryAcquireSRWLockExclusive) (gpointer lock); - void (* ReleaseSRWLockExclusive) (gpointer lock); - - void (* InitializeConditionVariable) (gpointer cond); - void (* DeleteConditionVariable) (gpointer cond); /* fake */ - BOOL (* SleepConditionVariableSRW) (gpointer cond, - gpointer lock, - DWORD timeout, - ULONG flags); - void (* WakeAllConditionVariable) (gpointer cond); - void (* 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) { - /* temporary until we fix libglib */ - if (mutex == NULL) - return; - - g_thread_impl_vtable.AcquireSRWLockExclusive (mutex); + AcquireSRWLockExclusive ((gpointer) mutex); } gboolean g_mutex_trylock (GMutex *mutex) { - /* temporary until we fix libglib */ - if (mutex == NULL) - return TRUE; - - return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex); + return TryAcquireSRWLockExclusive ((gpointer) mutex); } void g_mutex_unlock (GMutex *mutex) { - /* temporary until we fix libglib */ - if (mutex == NULL) - return; + ReleaseSRWLockExclusive ((gpointer) mutex); +} + +/* {{{1 GRecMutex */ + +static CRITICAL_SECTION * +g_rec_mutex_impl_new (void) +{ + CRITICAL_SECTION *cs; + + cs = g_slice_new (CRITICAL_SECTION); + InitializeCriticalSection (cs); - g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex); + return cs; } -/* {{{1 GCond */ -void -g_cond_init (GCond *cond) +static void +g_rec_mutex_impl_free (CRITICAL_SECTION *cs) { - g_thread_impl_vtable.InitializeConditionVariable (cond); + DeleteCriticalSection (cs); + g_slice_free (CRITICAL_SECTION, cs); } -void -g_cond_clear (GCond *cond) +static CRITICAL_SECTION * +g_rec_mutex_get_impl (GRecMutex *mutex) { - if (g_thread_impl_vtable.DeleteConditionVariable) - g_thread_impl_vtable.DeleteConditionVariable (cond); + 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_cond_signal (GCond *cond) +g_rec_mutex_init (GRecMutex *mutex) { - /* temporary until we fix libglib */ - if (cond == NULL) - return; - - g_thread_impl_vtable.WakeConditionVariable (cond); + mutex->p = g_rec_mutex_impl_new (); } void -g_cond_broadcast (GCond *cond) +g_rec_mutex_clear (GRecMutex *mutex) { - /* temporary until we fix libglib */ - if (cond == NULL) - return; + g_rec_mutex_impl_free (mutex->p); +} - g_thread_impl_vtable.WakeAllConditionVariable (cond); +void +g_rec_mutex_lock (GRecMutex *mutex) +{ + EnterCriticalSection (g_rec_mutex_get_impl (mutex)); } void -g_cond_wait (GCond *cond, - GMutex *entered_mutex) +g_rec_mutex_unlock (GRecMutex *mutex) { - g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, INFINITE, 0); + LeaveCriticalSection (mutex->p); } gboolean -g_cond_timedwait (GCond *cond, - GMutex *entered_mutex, - gint64 abs_time) +g_rec_mutex_trylock (GRecMutex *mutex) { - gint64 span; - FILETIME ft; - gint64 now; - - GetSystemTimeAsFileTime (&ft); - memmove (&now, &ft, sizeof (FILETIME)); - - now -= G_GINT64_CONSTANT (116444736000000000); - now /= 10; + return TryEnterCriticalSection (g_rec_mutex_get_impl (mutex)); +} - span = abs_time - now; +/* {{{1 GRWLock */ - if G_UNLIKELY (span < 0) - span = 0; +void +g_rw_lock_init (GRWLock *lock) +{ + InitializeSRWLock ((gpointer) lock); +} - if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * G_MAXINT32) - span = INFINITE; +void +g_rw_lock_clear (GRWLock *lock) +{ +} - return g_thread_impl_vtable.SleepConditionVariableSRW (cond, entered_mutex, span / 1000, 0); +void +g_rw_lock_writer_lock (GRWLock *lock) +{ + AcquireSRWLockExclusive ((gpointer) lock); } gboolean -g_cond_timed_wait (GCond *cond, - GMutex *entered_mutex, - GTimeVal *abs_time) +g_rw_lock_writer_trylock (GRWLock *lock) { - if (abs_time) - { - gint64 micros; - - micros = abs_time->tv_sec; - micros *= 1000000; - micros += abs_time->tv_usec; - - return g_cond_timedwait (cond, entered_mutex, micros); - } - else - { - g_cond_wait (cond, entered_mutex); - return TRUE; - } + return TryAcquireSRWLockExclusive ((gpointer) lock); } -/* {{{1 new/free API */ -GCond * -g_cond_new (void) +void +g_rw_lock_writer_unlock (GRWLock *lock) { - GCond *cond; - - /* malloc() is temporary until all libglib users are ported away */ - cond = malloc (sizeof (GCond)); - if G_UNLIKELY (cond == NULL) - g_thread_abort (errno, "malloc"); - g_cond_init (cond); - - return cond; + ReleaseSRWLockExclusive ((gpointer) lock); } void -g_cond_free (GCond *cond) +g_rw_lock_reader_lock (GRWLock *lock) { - g_cond_clear (cond); - free (cond); + AcquireSRWLockShared ((gpointer) lock); } -/* {{{1 GPrivate */ - -#include "glib.h" -#include "gthreadprivate.h" - -#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)) - -static DWORD g_thread_self_tls; -static DWORD g_private_tls; -static CRITICAL_SECTION g_thread_global_spinlock; - -typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); - -/* As noted in the docs, GPrivate is a limited resource, here we take - * a rather low maximum to save memory, use GStaticPrivate instead. */ -#define G_PRIVATE_MAX 100 - -static GDestroyNotify g_private_destructors[G_PRIVATE_MAX]; - -static guint g_private_next = 0; - -typedef struct _GThreadData GThreadData; -struct _GThreadData +gboolean +g_rw_lock_reader_trylock (GRWLock *lock) { - GThreadFunc func; - gpointer data; - HANDLE thread; - gboolean joinable; -}; + return TryAcquireSRWLockShared ((gpointer) lock); +} -static GPrivate * -g_private_new_win32_impl (GDestroyNotify destructor) +void +g_rw_lock_reader_unlock (GRWLock *lock) { - GPrivate *result; - EnterCriticalSection (&g_thread_global_spinlock); - if (g_private_next >= G_PRIVATE_MAX) - { - char buf[100]; - sprintf (buf, - "Too many GPrivate allocated. Their number is limited to %d.", - G_PRIVATE_MAX); - MessageBox (NULL, buf, NULL, MB_ICONERROR|MB_SETFOREGROUND); - if (IsDebuggerPresent ()) - G_BREAKPOINT (); - abort (); - } - g_private_destructors[g_private_next] = destructor; - result = GUINT_TO_POINTER (g_private_next); - g_private_next++; - LeaveCriticalSection (&g_thread_global_spinlock); - - return result; + ReleaseSRWLockShared ((gpointer) lock); } -/* NOTE: the functions g_private_get and g_private_set may not use - functions from gmem.c and gmessages.c */ - -static void -g_private_set_win32_impl (GPrivate * private_key, gpointer value) +/* {{{1 GCond */ +void +g_cond_init (GCond *cond) { - gpointer* array = TlsGetValue (g_private_tls); - guint index = GPOINTER_TO_UINT (private_key); - - if (index >= G_PRIVATE_MAX) - return; - - if (!array) - { - array = (gpointer*) calloc (G_PRIVATE_MAX, sizeof (gpointer)); - TlsSetValue (g_private_tls, array); - } - - array[index] = value; + InitializeConditionVariable ((gpointer) cond); } -static gpointer -g_private_get_win32_impl (GPrivate * private_key) +void +g_cond_clear (GCond *cond) { - gpointer* array = TlsGetValue (g_private_tls); - guint index = GPOINTER_TO_UINT (private_key); - - if (index >= G_PRIVATE_MAX || !array) - return NULL; - - return array[index]; } -/* {{{1 GThread */ - -static void -g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority) +void +g_cond_signal (GCond *cond) { - GThreadData *target = *(GThreadData **)thread; - gint native_prio; - - switch (priority) - { - case G_THREAD_PRIORITY_LOW: - native_prio = THREAD_PRIORITY_BELOW_NORMAL; - break; - - case G_THREAD_PRIORITY_NORMAL: - native_prio = THREAD_PRIORITY_NORMAL; - break; - - case G_THREAD_PRIORITY_HIGH: - native_prio = THREAD_PRIORITY_ABOVE_NORMAL; - break; - - case G_THREAD_PRIORITY_URGENT: - native_prio = THREAD_PRIORITY_HIGHEST; - break; - - default: - g_return_if_reached (); - } - - win32_check_for_error (SetThreadPriority (target->thread, native_prio)); + WakeConditionVariable ((gpointer) cond); } -static void -g_thread_self_win32_impl (gpointer thread) +void +g_cond_broadcast (GCond *cond) { - GThreadData *self = TlsGetValue (g_thread_self_tls); - - if (!self) - { - /* This should only happen for the main thread! */ - HANDLE handle = GetCurrentThread (); - HANDLE process = GetCurrentProcess (); - self = g_new (GThreadData, 1); - win32_check_for_error (DuplicateHandle (process, handle, process, - &self->thread, 0, FALSE, - DUPLICATE_SAME_ACCESS)); - win32_check_for_error (TlsSetValue (g_thread_self_tls, self)); - self->func = NULL; - self->data = NULL; - self->joinable = FALSE; - } - - *(GThreadData **)thread = self; + WakeAllConditionVariable ((gpointer) cond); } -static void -g_thread_exit_win32_impl (void) +void +g_cond_wait (GCond *cond, + GMutex *entered_mutex) { - GThreadData *self = TlsGetValue (g_thread_self_tls); - guint i, private_max; - gpointer *array = TlsGetValue (g_private_tls); - - EnterCriticalSection (&g_thread_global_spinlock); - private_max = g_private_next; - LeaveCriticalSection (&g_thread_global_spinlock); - - if (array) - { - gboolean some_data_non_null; - - do { - some_data_non_null = FALSE; - for (i = 0; i < private_max; i++) - { - GDestroyNotify destructor = g_private_destructors[i]; - GDestroyNotify data = array[i]; - - if (data) - some_data_non_null = TRUE; - - array[i] = NULL; - - if (destructor && data) - destructor (data); - } - } while (some_data_non_null); - - free (array); + SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, INFINITE, 0); +} - win32_check_for_error (TlsSetValue (g_private_tls, NULL)); - } +gboolean +g_cond_wait_until (GCond *cond, + GMutex *entered_mutex, + gint64 end_time) +{ + gint64 span, start_time; + DWORD span_millis; + gboolean signalled; - if (self) + start_time = g_get_monotonic_time (); + do { - if (!self->joinable) - { - win32_check_for_error (CloseHandle (self->thread)); - g_free (self); - } - win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL)); + span = end_time - start_time; + + if G_UNLIKELY (span < 0) + span_millis = 0; + else if G_UNLIKELY (span > G_GINT64_CONSTANT (1000) * (DWORD) INFINITE) + span_millis = INFINITE; + else + /* Round up so we don't time out too early */ + span_millis = (span + 1000 - 1) / 1000; + + /* We never want to wait infinitely */ + if (span_millis >= INFINITE) + span_millis = INFINITE - 1; + + signalled = SleepConditionVariableSRW ((gpointer) cond, (gpointer) entered_mutex, span_millis, 0); + if (signalled) + break; + + /* In case we didn't wait long enough after a timeout, wait again for the + * remaining time */ + start_time = g_get_monotonic_time (); } + while (start_time < end_time); - if (g_thread_impl_vtable.CallThisOnThreadExit) - g_thread_impl_vtable.CallThisOnThreadExit (); - - _endthreadex (0); + return signalled; } -static guint __stdcall -g_thread_proxy (gpointer data) -{ - GThreadData *self = (GThreadData*) data; - - win32_check_for_error (TlsSetValue (g_thread_self_tls, self)); - - self->func (self->data); - - g_thread_exit_win32_impl (); - - g_assert_not_reached (); +/* {{{1 GPrivate */ - return 0; -} +typedef struct _GPrivateDestructor GPrivateDestructor; -static void -g_thread_create_win32_impl (GThreadFunc func, - gpointer data, - gulong stack_size, - gboolean joinable, - gboolean bound, - GThreadPriority priority, - gpointer thread, - GError **error) +struct _GPrivateDestructor { - guint ignore; - GThreadData *retval; - - g_return_if_fail (func); - g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW); - g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT); - - retval = g_new(GThreadData, 1); - retval->func = func; - retval->data = data; + DWORD index; + GDestroyNotify notify; + GPrivateDestructor *next; +}; - retval->joinable = joinable; +static GPrivateDestructor *g_private_destructors; /* (atomic) prepend-only */ +static CRITICAL_SECTION g_private_lock; - retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy, - retval, 0, &ignore); +static DWORD +g_private_get_impl (GPrivate *key) +{ + DWORD impl = (DWORD) GPOINTER_TO_UINT(key->p); - if (retval->thread == NULL) + if G_UNLIKELY (impl == 0) { - 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 (retval); - g_free (win_error); - return; + EnterCriticalSection (&g_private_lock); + impl = (UINT_PTR) key->p; + if (impl == 0) + { + GPrivateDestructor *destructor; + + impl = TlsAlloc (); + + if G_UNLIKELY (impl == 0) + { + /* Ignore TLS index 0 temporarily (as 0 is the indicator that we + * haven't allocated TLS yet) and alloc again; + * See https://gitlab.gnome.org/GNOME/glib/-/issues/2058 */ + DWORD impl2 = TlsAlloc (); + TlsFree (impl); + impl = impl2; + } + + if (impl == TLS_OUT_OF_INDEXES || impl == 0) + 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_atomic_pointer_get (&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 (!g_atomic_pointer_compare_and_exchange (&g_private_destructors, + destructor->next, + destructor)) + g_thread_abort (0, "g_private_get_impl(1)"); + } + + /* Ditto, due to the unlocked access on the fast path */ + if (!g_atomic_pointer_compare_and_exchange (&key->p, NULL, GUINT_TO_POINTER (impl))) + g_thread_abort (0, "g_private_get_impl(2)"); + } + LeaveCriticalSection (&g_private_lock); } - *(GThreadData **)thread = retval; + return impl; +} - g_thread_set_priority_win32_impl (thread, priority); +gpointer +g_private_get (GPrivate *key) +{ + return TlsGetValue (g_private_get_impl (key)); } -static void -g_thread_yield_win32_impl (void) +void +g_private_set (GPrivate *key, + gpointer value) { - Sleep(0); + TlsSetValue (g_private_get_impl (key), value); } -static void -g_thread_join_win32_impl (gpointer thread) +void +g_private_replace (GPrivate *key, + gpointer value) { - GThreadData *target = *(GThreadData **)thread; + DWORD impl = g_private_get_impl (key); + gpointer old; - g_return_if_fail (target->joinable); + old = TlsGetValue (impl); + TlsSetValue (impl, value); + if (old && key->notify) + key->notify (old); +} - win32_check_for_error (WAIT_FAILED != - WaitForSingleObject (target->thread, INFINITE)); +/* {{{1 GThread */ - win32_check_for_error (CloseHandle (target->thread)); - g_free (target); -} +#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 -/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */ +#define G_MUTEX_SIZE (sizeof (gpointer)) -static DWORD g_thread_xp_waiter_tls; -static CRITICAL_SECTION g_thread_xp_lock; +typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); -/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */ -typedef struct _GThreadXpWaiter GThreadXpWaiter; -struct _GThreadXpWaiter +typedef struct { - HANDLE event; - volatile GThreadXpWaiter *next; -}; + GRealThread thread; + + GThreadFunc proxy; + HANDLE handle; +} GThreadWin32; -static GThreadXpWaiter * -g_thread_xp_waiter_get (void) +void +g_system_thread_free (GRealThread *thread) { - GThreadXpWaiter *waiter; + GThreadWin32 *wt = (GThreadWin32 *) thread; - waiter = TlsGetValue (g_thread_xp_waiter_tls); + win32_check_for_error (CloseHandle (wt->handle)); + g_slice_free (GThreadWin32, wt); +} - 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"); - - TlsSetValue (g_thread_xp_waiter_tls, waiter); - } +void +g_system_thread_exit (void) +{ + /* In static compilation, DllMain doesn't exist and so DLL_THREAD_DETACH + * case is never called and thread destroy notifications are not triggered. + * To ensure that notifications are correctly triggered in static + * compilation mode, we call directly the "detach" function here right + * before terminating the thread. + * As all win32 threads initialized through the glib API are run through + * the same proxy function g_thread_win32_proxy() which calls systematically + * g_system_thread_exit() when finishing, we obtain the same behavior as + * with dynamic compilation. + * + * WARNING: unfortunately this mechanism cannot work with threads created + * directly from the Windows API using CreateThread() or _beginthread/ex(). + * It only works with threads created by using the glib API with + * g_system_thread_new(). If users need absolutely to use a thread NOT + * created with glib API under Windows and in static compilation mode, they + * should not use glib functions within their thread or they may encounter + * memory leaks when the thread finishes. + */ +#ifdef GLIB_STATIC_COMPILATION + g_thread_win32_thread_detach (); +#endif - return waiter; + _endthreadex (0); } -static void -g_thread_xp_CallThisOnThreadExit (void) +static guint __stdcall +g_thread_win32_proxy (gpointer data) { - GThreadXpWaiter *waiter; + GThreadWin32 *self = data; - waiter = TlsGetValue (g_thread_xp_waiter_tls); + self->proxy (self); - if (waiter != NULL) - { - TlsSetValue (g_thread_xp_waiter_tls, NULL); - CloseHandle (waiter->event); - free (waiter); - } -} + g_system_thread_exit (); -/* {{{2 SRWLock emulation */ -typedef struct -{ - CRITICAL_SECTION critical_section; -} GThreadSRWLock; + g_assert_not_reached (); -static void -g_thread_xp_InitializeSRWLock (gpointer mutex) -{ - *(GThreadSRWLock * volatile *) mutex = NULL; + return 0; } -static void -g_thread_xp_DeleteSRWLock (gpointer mutex) +GRealThread * +g_system_thread_new (GThreadFunc proxy, + gulong stack_size, + const char *name, + GThreadFunc func, + gpointer data, + GError **error) { - GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; - - if (lock) + GThreadWin32 *thread; + GRealThread *base_thread; + guint ignore; + const gchar *message = NULL; + int thread_prio; + + thread = g_slice_new0 (GThreadWin32); + thread->proxy = proxy; + thread->handle = (HANDLE) NULL; + base_thread = (GRealThread*)thread; + base_thread->ref_count = 2; + base_thread->ours = TRUE; + base_thread->thread.joinable = TRUE; + base_thread->thread.func = func; + base_thread->thread.data = data; + base_thread->name = g_strdup (name); + + thread->handle = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_win32_proxy, thread, + CREATE_SUSPENDED, &ignore); + + if (thread->handle == NULL) { - DeleteCriticalSection (&lock->critical_section); - free (lock); + message = "Error creating thread"; + goto error; } -} -static GThreadSRWLock * -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. + /* For thread priority inheritance we need to manually set the thread + * priority of the new thread to the priority of the current thread. We + * also have to start the thread suspended and resume it after actually + * setting the priority here. + * + * On Windows, by default all new threads are created with NORMAL thread + * priority. */ - result = *lock; + { + HANDLE current_thread = GetCurrentThread (); + thread_prio = GetThreadPriority (current_thread); + } - if G_UNLIKELY (result == NULL) + if (thread_prio == THREAD_PRIORITY_ERROR_RETURN) { - EnterCriticalSection (&g_thread_xp_lock); - - result = malloc (sizeof (GThreadSRWLock)); - - if (result == NULL) - g_thread_abort (errno, "malloc"); + message = "Error getting current thread priority"; + goto error; + } - InitializeCriticalSection (&result->critical_section); - *lock = result; + if (SetThreadPriority (thread->handle, thread_prio) == 0) + { + message = "Error setting new thread priority"; + goto error; + } - LeaveCriticalSection (&g_thread_xp_lock); + if (ResumeThread (thread->handle) == (DWORD) -1) + { + message = "Error resuming new thread"; + goto error; } - return result; + return (GRealThread *) thread; + +error: + { + gchar *win_error = g_win32_error_message (GetLastError ()); + g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, + "%s: %s", message, win_error); + g_free (win_error); + if (thread->handle) + CloseHandle (thread->handle); + g_slice_free (GThreadWin32, thread); + return NULL; + } } -static void -g_thread_xp_AcquireSRWLockExclusive (gpointer mutex) +void +g_thread_yield (void) { - GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); - - EnterCriticalSection (&lock->critical_section); + Sleep(0); } -static BOOLEAN -g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex) +void +g_system_thread_wait (GRealThread *thread) { - GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + GThreadWin32 *wt = (GThreadWin32 *) thread; - return TryEnterCriticalSection (&lock->critical_section); + win32_check_for_error (WAIT_FAILED != WaitForSingleObject (wt->handle, INFINITE)); } -static void -g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex) -{ - GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; +#define EXCEPTION_SET_THREAD_NAME ((DWORD) 0x406D1388) - /* We need this until we fix some weird parts of GLib that try to - * unlock freshly-allocated mutexes. - */ - if (lock != NULL) - LeaveCriticalSection (&lock->critical_section); -} +#ifndef _MSC_VER +static void *SetThreadName_VEH_handle = NULL; -/* {{{2 CONDITION_VARIABLE emulation */ -typedef struct +static LONG __stdcall +SetThreadName_VEH (PEXCEPTION_POINTERS ExceptionInfo) { - volatile GThreadXpWaiter *first; - volatile GThreadXpWaiter **last_ptr; -} GThreadXpCONDITION_VARIABLE; + if (ExceptionInfo->ExceptionRecord != NULL && + ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SET_THREAD_NAME) + return EXCEPTION_CONTINUE_EXECUTION; -static void -g_thread_xp_InitializeConditionVariable (gpointer cond) -{ - *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL; + return EXCEPTION_CONTINUE_SEARCH; } +#endif -static void -g_thread_xp_DeleteConditionVariable (gpointer cond) +typedef struct _THREADNAME_INFO { - GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond; + DWORD dwType; /* must be 0x1000 */ + LPCSTR szName; /* pointer to name (in user addr space) */ + DWORD dwThreadID; /* thread ID (-1=caller thread) */ + DWORD dwFlags; /* reserved for future use, must be zero */ +} THREADNAME_INFO; - if (cv) - free (cv); -} +static void +SetThreadName (DWORD dwThreadID, + LPCSTR szThreadName) +{ + THREADNAME_INFO info; + DWORD infosize; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + infosize = sizeof (info) / sizeof (DWORD); + +#ifdef _MSC_VER + __try + { + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, + (const ULONG_PTR *) &info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } +#else + /* Without a debugger we *must* have an exception handler, + * otherwise raising an exception will crash the process. + */ + if ((!IsDebuggerPresent ()) && (SetThreadName_VEH_handle == NULL)) + return; + + RaiseException (EXCEPTION_SET_THREAD_NAME, 0, infosize, (const ULONG_PTR *) &info); +#endif +} + +typedef HRESULT (WINAPI *pSetThreadDescription) (HANDLE hThread, + PCWSTR lpThreadDescription); +static pSetThreadDescription SetThreadDescriptionFunc = NULL; +static HMODULE kernel32_module = NULL; -static GThreadXpCONDITION_VARIABLE * -g_thread_xp_get_condition_variable (GThreadXpCONDITION_VARIABLE * volatile *cond) +static gboolean +g_thread_win32_load_library (void) { - 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) + /* FIXME: Add support for UWP app */ +#if !defined(G_WINAPI_ONLY_APP) + static gsize _init_once = 0; + if (g_once_init_enter (&_init_once)) { - 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) + kernel32_module = LoadLibraryW (L"kernel32.dll"); + if (kernel32_module) { - free (result); - result = *cond; + SetThreadDescriptionFunc = + (pSetThreadDescription) GetProcAddress (kernel32_module, + "SetThreadDescription"); + if (!SetThreadDescriptionFunc) + FreeLibrary (kernel32_module); } + g_once_init_leave (&_init_once, 1); } +#endif - return result; + return !!SetThreadDescriptionFunc; } -static BOOL -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); - *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); - - return status == WAIT_OBJECT_0; -} - -static void -g_thread_xp_WakeConditionVariable (gpointer cond) +static gboolean +g_thread_win32_set_thread_desc (const gchar *name) { - GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); - volatile GThreadXpWaiter *waiter; - - EnterCriticalSection (&g_thread_xp_lock); - waiter = cv->first; - if (waiter != NULL) - { - cv->first = waiter->next; - if (cv->first == NULL) - cv->last_ptr = &cv->first; - } - LeaveCriticalSection (&g_thread_xp_lock); + HRESULT hr; + wchar_t *namew; - if (waiter != NULL) - SetEvent (waiter->event); -} - -static void -g_thread_xp_WakeAllConditionVariable (gpointer cond) -{ - GThreadXpCONDITION_VARIABLE *cv = g_thread_xp_get_condition_variable (cond); - volatile GThreadXpWaiter *waiter; + if (!g_thread_win32_load_library () || !name) + return FALSE; - EnterCriticalSection (&g_thread_xp_lock); - waiter = cv->first; - cv->first = NULL; - cv->last_ptr = &cv->first; - LeaveCriticalSection (&g_thread_xp_lock); + namew = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL); + if (!namew) + return FALSE; - while (waiter != NULL) - { - volatile GThreadXpWaiter *next; + hr = SetThreadDescriptionFunc (GetCurrentThread (), namew); - next = waiter->next; - SetEvent (waiter->event); - waiter = next; - } + g_free (namew); + return SUCCEEDED (hr); } -/* {{{2 XP Setup */ -static void -g_thread_xp_init (void) +void +g_system_thread_set_name (const gchar *name) { - 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_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; + /* Prefer SetThreadDescription over exception based way if available, + * since thread description set by SetThreadDescription will be preserved + * in dump file */ + if (!g_thread_win32_set_thread_desc (name)) + SetThreadName ((DWORD) -1, name); } /* {{{1 Epilogue */ -GThreadFunctions g_thread_functions_for_glib_use = -{ - g_mutex_new, /* mutex */ - g_mutex_lock, - g_mutex_trylock, - g_mutex_unlock, - g_mutex_free, - g_cond_new, /* condition */ - g_cond_signal, - g_cond_broadcast, - g_cond_wait, - g_cond_timed_wait, - g_cond_free, - g_private_new_win32_impl, /* private thread data */ - g_private_get_win32_impl, - g_private_set_win32_impl, - g_thread_create_win32_impl, /* thread */ - g_thread_yield_win32_impl, - g_thread_join_win32_impl, - g_thread_exit_win32_impl, - g_thread_set_priority_win32_impl, - g_thread_self_win32_impl, - NULL /* no equal function necessary */ -}; - void -_g_thread_impl_init (void) +g_thread_win32_init (void) { - static gboolean beenhere = FALSE; - - if (beenhere) - return; - - beenhere = TRUE; + InitializeCriticalSection (&g_private_lock); - printf ("thread init\n"); - win32_check_for_error (TLS_OUT_OF_INDEXES != - (g_thread_self_tls = TlsAlloc ())); - win32_check_for_error (TLS_OUT_OF_INDEXES != - (g_private_tls = TlsAlloc ())); - InitializeCriticalSection (&g_thread_global_spinlock); +#ifndef _MSC_VER + SetThreadName_VEH_handle = AddVectoredExceptionHandler (1, &SetThreadName_VEH); + if (SetThreadName_VEH_handle == NULL) + { + /* This is bad, but what can we do? */ + } +#endif } -static gboolean -g_thread_lookup_native_funcs (void) +void +g_thread_win32_thread_detach (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(InitializeConditionVariable); - GET_FUNC(SleepConditionVariableSRW); - GET_FUNC(WakeAllConditionVariable); - GET_FUNC(WakeConditionVariable); -#undef GET_FUNC + gboolean dtors_called; - g_thread_impl_vtable = native_vtable; - - return TRUE; + 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_atomic_pointer_get (&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); } -G_GNUC_INTERNAL void -g_thread_DllMain (void) +void +g_thread_win32_process_detach (void) { - /* XXX This is broken right now for some unknown reason... - - if (g_thread_lookup_native_funcs ()) - fprintf (stderr, "(debug) GThread using native mode\n"); - else -*/ +#ifndef _MSC_VER + if (SetThreadName_VEH_handle != NULL) { - fprintf (stderr, "(debug) GThread using Windows XP mode\n"); - g_thread_xp_init (); + RemoveVectoredExceptionHandler (SetThreadName_VEH_handle); + SetThreadName_VEH_handle = NULL; } +#endif } /* vim:set foldmethod=marker: */ -