diff --git a/glib/glib.symbols b/glib/glib.symbols index ab943bb1a..33b72de1a 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -1594,3 +1594,19 @@ glib_micro_version glib_minor_version glib_on_error_halt g_mem_gc_friendly +g_cond_broadcast +g_cond_clear +g_cond_free +g_cond_init +g_cond_new +g_cond_signal +g_cond_timedwait +g_cond_timed_wait +g_cond_wait +g_mutex_clear +g_mutex_free +g_mutex_init +g_mutex_lock +g_mutex_new +g_mutex_trylock +g_mutex_unlock diff --git a/glib/gthread-posix.c b/glib/gthread-posix.c index 96abcf80e..1fcc3dd33 100644 --- a/glib/gthread-posix.c +++ b/glib/gthread-posix.c @@ -27,12 +27,248 @@ * GLib at ftp://ftp.gtk.org/pub/gtk/. */ -/* - * MT safe +/* 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 "gthread.h" + +#include +#include +#include +#include +#include + +static void +g_thread_abort (gint status, + const gchar *function) +{ + fprintf (stderr, "GLib (gthread-posix.c): Unexpected error from C library during '%s': %s. Aborting.\n", + strerror (status), function); + abort (); +} + +/* {{{1 GMutex */ +void +g_mutex_init (GMutex *mutex) +{ + gint status; + + if G_UNLIKELY ((status = pthread_mutex_init (&mutex->impl, NULL)) != 0) + g_thread_abort (status, "pthread_mutex_init"); +} + +void +g_mutex_clear (GMutex *mutex) +{ + gint status; + + if G_UNLIKELY ((status = pthread_mutex_destroy (&mutex->impl)) != 0) + g_thread_abort (status, "pthread_mutex_destroy"); +} + +void +g_mutex_lock (GMutex *mutex) +{ + gint status; + + /* temporary until we fix libglib */ + if (mutex == NULL) + return; + + if G_UNLIKELY ((status = pthread_mutex_lock (&mutex->impl)) != 0) + g_thread_abort (status, "pthread_mutex_lock"); +} + +void +g_mutex_unlock (GMutex *mutex) +{ + gint status; + + /* temporary until we fix libglib */ + if (mutex == NULL) + return; + + if G_UNLIKELY ((status = pthread_mutex_unlock (&mutex->impl)) != 0) + g_thread_abort (status, "pthread_mutex_lock"); +} + +gboolean +g_mutex_trylock (GMutex *mutex) +{ + gint status; + + /* temporary until we fix libglib */ + if (mutex == NULL) + return TRUE; + + if G_LIKELY ((status = pthread_mutex_trylock (&mutex->impl)) == 0) + return TRUE; + + if G_UNLIKELY (status != EBUSY) + g_thread_abort (status, "pthread_mutex_trylock"); + + return FALSE; +} + +/* {{{1 GCond */ + +void +g_cond_init (GCond *cond) +{ + gint status; + + if G_UNLIKELY ((status = pthread_cond_init (&cond->impl, NULL)) != 0) + g_thread_abort (status, "pthread_cond_init"); +} + +void +g_cond_clear (GCond *cond) +{ + gint status; + + if G_UNLIKELY ((status = pthread_cond_destroy (&cond->impl)) != 0) + g_thread_abort (status, "pthread_cond_destroy"); +} + +void +g_cond_wait (GCond *cond, + GMutex *mutex) +{ + gint status; + + if G_UNLIKELY ((status = pthread_cond_wait (&cond->impl, &mutex->impl)) != 0) + g_thread_abort (status, "pthread_cond_wait"); +} + +void +g_cond_signal (GCond *cond) +{ + gint status; + + /* temporary until we fix libglib */ + if (cond == NULL) + return; + + if G_UNLIKELY ((status = pthread_cond_signal (&cond->impl)) != 0) + g_thread_abort (status, "pthread_cond_signal"); +} + +void +g_cond_broadcast (GCond *cond) +{ + gint status; + + /* temporary until we fix libglib */ + if (cond == NULL) + return; + + if G_UNLIKELY ((status = pthread_cond_broadcast (&cond->impl)) != 0) + g_thread_abort (status, "pthread_cond_broadcast"); +} + +gboolean +g_cond_timed_wait (GCond *cond, + GMutex *mutex, + GTimeVal *abs_time) +{ + struct timespec end_time; + gint status; + + if (abs_time == NULL) + { + g_cond_wait (cond, mutex); + return TRUE; + } + + end_time.tv_sec = abs_time->tv_sec; + end_time.tv_nsec = abs_time->tv_usec * 1000; + + if ((status = pthread_cond_timedwait (&cond->impl, &mutex->impl, &end_time)) == 0) + return TRUE; + + if G_UNLIKELY (status != ETIMEDOUT) + g_thread_abort (status, "pthread_cond_timedwait"); + + return FALSE; +} + +gboolean +g_cond_timedwait (GCond *cond, + GMutex *mutex, + gint64 abs_time) +{ + struct timespec end_time; + gint status; + + end_time.tv_sec = abs_time / 1000000; + end_time.tv_nsec = (abs_time % 1000000) * 1000; + + if ((status = pthread_cond_timedwait (&cond->impl, &mutex->impl, &end_time)) == 0) + return TRUE; + + if G_UNLIKELY (status != ETIMEDOUT) + g_thread_abort (status, "pthread_cond_timedwait"); + + return FALSE; +} + +/* {{{1 new/free API */ + +GMutex * +g_mutex_new (void) +{ + GMutex *mutex; + + /* malloc() is temporary until all libglib users are ported away */ + mutex = malloc (sizeof (GMutex)); + if G_UNLIKELY (mutex == NULL) + g_thread_abort (errno, "malloc"); + g_mutex_init (mutex); + + return mutex; +} + +void +g_mutex_free (GMutex *mutex) +{ + g_mutex_clear (mutex); + free (mutex); +} + +GCond * +g_cond_new (void) +{ + 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; +} + +void +g_cond_free (GCond *cond) +{ + g_cond_clear (cond); + free (cond); +} + +/* {{{1 GPrivate */ + #include "glib.h" #include "gthreadprivate.h" @@ -148,101 +384,6 @@ _g_thread_impl_init(void) #endif /* HAVE_PRIORITIES */ } -static GMutex * -g_mutex_new_posix_impl (void) -{ - GMutex *result = (GMutex *) g_new (pthread_mutex_t, 1); - posix_check_cmd (pthread_mutex_init ((pthread_mutex_t *) result, NULL)); - return result; -} - -static void -g_mutex_free_posix_impl (GMutex * mutex) -{ - posix_check_cmd (pthread_mutex_destroy ((pthread_mutex_t *) mutex)); - g_free (mutex); -} - -/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use - functions from gmem.c and gmessages.c; */ - -/* pthread_mutex_lock, pthread_mutex_unlock can be taken directly, as - signature and semantic are right, but without error check then!!!!, - we might want to change this therefore. */ - -static gboolean -g_mutex_trylock_posix_impl (GMutex * mutex) -{ - int result; - - result = pthread_mutex_trylock ((pthread_mutex_t *) mutex); - - if (result == EBUSY) - return FALSE; - - posix_check_err (result, "pthread_mutex_trylock"); - return TRUE; -} - -static GCond * -g_cond_new_posix_impl (void) -{ - GCond *result = (GCond *) g_new (pthread_cond_t, 1); - posix_check_cmd (pthread_cond_init ((pthread_cond_t *) result, NULL)); - return result; -} - -/* pthread_cond_signal, pthread_cond_broadcast and pthread_cond_wait - can be taken directly, as signature and semantic are right, but - without error check then!!!!, we might want to change this - therefore. */ - -#define G_NSEC_PER_SEC 1000000000 - -static gboolean -g_cond_timed_wait_posix_impl (GCond * cond, - GMutex * entered_mutex, - GTimeVal * abs_time) -{ - int result; - struct timespec end_time; - gboolean timed_out; - - g_return_val_if_fail (cond != NULL, FALSE); - g_return_val_if_fail (entered_mutex != NULL, FALSE); - - if (!abs_time) - { - result = pthread_cond_wait ((pthread_cond_t *)cond, - (pthread_mutex_t *) entered_mutex); - timed_out = FALSE; - } - else - { - end_time.tv_sec = abs_time->tv_sec; - end_time.tv_nsec = abs_time->tv_usec * (G_NSEC_PER_SEC / G_USEC_PER_SEC); - - g_return_val_if_fail (end_time.tv_nsec < G_NSEC_PER_SEC, TRUE); - - result = pthread_cond_timedwait ((pthread_cond_t *) cond, - (pthread_mutex_t *) entered_mutex, - &end_time); - timed_out = (result == ETIMEDOUT); - } - - if (!timed_out) - posix_check_err (result, "pthread_cond_timedwait"); - - return !timed_out; -} - -static void -g_cond_free_posix_impl (GCond * cond) -{ - posix_check_cmd (pthread_cond_destroy ((pthread_cond_t *) cond)); - g_free (cond); -} - static GPrivate * g_private_new_posix_impl (GDestroyNotify destructor) { @@ -271,6 +412,9 @@ g_private_get_posix_impl (GPrivate * private_key) return pthread_getspecific (*(pthread_key_t *) private_key); } +/* {{{1 GThread */ + + static void g_thread_create_posix_impl (GThreadFunc thread_func, gpointer arg, @@ -381,19 +525,20 @@ g_thread_equal_posix_impl (gpointer thread1, gpointer thread2) return (pthread_equal (*(pthread_t*)thread1, *(pthread_t*)thread2) != 0); } +/* {{{1 Epilogue */ GThreadFunctions g_thread_functions_for_glib_use = { - g_mutex_new_posix_impl, - (void (*)(GMutex *)) pthread_mutex_lock, - g_mutex_trylock_posix_impl, - (void (*)(GMutex *)) pthread_mutex_unlock, - g_mutex_free_posix_impl, - g_cond_new_posix_impl, - (void (*)(GCond *)) pthread_cond_signal, - (void (*)(GCond *)) pthread_cond_broadcast, - (void (*)(GCond *, GMutex *)) pthread_cond_wait, - g_cond_timed_wait_posix_impl, - g_cond_free_posix_impl, + g_mutex_new, + g_mutex_lock, + g_mutex_trylock, + g_mutex_unlock, + g_mutex_free, + g_cond_new, + g_cond_signal, + g_cond_broadcast, + g_cond_wait, + g_cond_timed_wait, + g_cond_free, g_private_new_posix_impl, g_private_get_posix_impl, g_private_set_posix_impl, @@ -405,3 +550,5 @@ GThreadFunctions g_thread_functions_for_glib_use = g_thread_self_posix_impl, g_thread_equal_posix_impl }; + +/* vim:set foldmethod=marker: */ diff --git a/glib/gthread-win32.c b/glib/gthread-win32.c index d50aee184..a880e1042 100644 --- a/glib/gthread-win32.c +++ b/glib/gthread-win32.c @@ -28,24 +28,278 @@ * GLib at ftp://ftp.gtk.org/pub/gtk/. */ -/* - * MT safe +/* 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 "gthreadprivate.h" +#include "gthread.h" -#define STRICT #define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */ #include -#undef STRICT #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. 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). + */ +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); +} + +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); +} + +gboolean +g_mutex_trylock (GMutex *mutex) +{ + /* temporary until we fix libglib */ + if (mutex == NULL) + return TRUE; + + return g_thread_impl_vtable.TryAcquireSRWLockExclusive (mutex); +} + +void +g_mutex_unlock (GMutex *mutex) +{ + /* temporary until we fix libglib */ + if (mutex == NULL) + return; + + g_thread_impl_vtable.ReleaseSRWLockExclusive (mutex); +} + +/* {{{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) +{ + /* temporary until we fix libglib */ + if (cond == NULL) + return; + + g_thread_impl_vtable.WakeConditionVariable (cond); +} + +void +g_cond_broadcast (GCond *cond) +{ + /* temporary until we fix libglib */ + if (cond == NULL) + return; + + 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_timedwait (GCond *cond, + GMutex *entered_mutex, + gint64 abs_time) +{ + gint64 span; + FILETIME ft; + gint64 now; + + GetSystemTimeAsFileTime (&ft); + memmove (&now, &ft, sizeof (FILETIME)); + + now -= G_GINT64_CONSTANT (116444736000000000); + now /= 10; + + span = abs_time - now; + + 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); +} + +gboolean +g_cond_timed_wait (GCond *cond, + GMutex *entered_mutex, + GTimeVal *abs_time) +{ + 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; + } +} + +/* {{{1 new/free API */ +GMutex * +g_mutex_new (void) +{ + GMutex *mutex; + + /* malloc() is temporary until all libglib users are ported away */ + mutex = malloc (sizeof (GMutex)); + if G_UNLIKELY (mutex == NULL) + g_thread_abort (errno, "malloc"); + g_mutex_init (mutex); + + return mutex; +} + +void +g_mutex_free (GMutex *mutex) +{ + g_mutex_clear (mutex); + free (mutex); +} + +GCond * +g_cond_new (void) +{ + 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; +} + +void +g_cond_free (GCond *cond) +{ + g_cond_clear (cond); + free (cond); +} + +/* {{{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", \ @@ -57,7 +311,6 @@ static DWORD g_thread_self_tls; static DWORD g_private_tls; -static DWORD g_cond_event_tls; static CRITICAL_SECTION g_thread_global_spinlock; typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *); @@ -79,195 +332,6 @@ struct _GThreadData gboolean joinable; }; -struct _GCond -{ - GPtrArray *array; - CRITICAL_SECTION lock; -}; - -static GMutex * -g_mutex_new_win32_impl (void) -{ - CRITICAL_SECTION *cs = g_new (CRITICAL_SECTION, 1); - gpointer *retval = g_new (gpointer, 1); - - InitializeCriticalSection (cs); - *retval = cs; - return (GMutex *) retval; -} - -static void -g_mutex_free_win32_impl (GMutex *mutex) -{ - gpointer *ptr = (gpointer *) mutex; - CRITICAL_SECTION *cs = (CRITICAL_SECTION *) *ptr; - - DeleteCriticalSection (cs); - g_free (cs); - g_free (mutex); -} - -/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use - functions from gmem.c and gmessages.c; */ - -static void -g_mutex_lock_win32_impl (GMutex *mutex) -{ - EnterCriticalSection (*(CRITICAL_SECTION **)mutex); -} - -static gboolean -g_mutex_trylock_win32_impl (GMutex * mutex) -{ - return TryEnterCriticalSection (*(CRITICAL_SECTION **)mutex); -} - -static void -g_mutex_unlock_win32_impl (GMutex *mutex) -{ - LeaveCriticalSection (*(CRITICAL_SECTION **)mutex); -} - -static GCond * -g_cond_new_win32_impl (void) -{ - GCond *retval = g_new (GCond, 1); - - retval->array = g_ptr_array_new (); - InitializeCriticalSection (&retval->lock); - - return retval; -} - -static void -g_cond_signal_win32_impl (GCond * cond) -{ - EnterCriticalSection (&cond->lock); - - if (cond->array->len > 0) - { - SetEvent (g_ptr_array_index (cond->array, 0)); - g_ptr_array_remove_index (cond->array, 0); - } - - LeaveCriticalSection (&cond->lock); -} - -static void -g_cond_broadcast_win32_impl (GCond * cond) -{ - guint i; - EnterCriticalSection (&cond->lock); - - for (i = 0; i < cond->array->len; i++) - SetEvent (g_ptr_array_index (cond->array, i)); - - g_ptr_array_set_size (cond->array, 0); - LeaveCriticalSection (&cond->lock); -} - -static gboolean -g_cond_wait_internal (GCond *cond, - GMutex *entered_mutex, - gulong milliseconds) -{ - gulong retval; - HANDLE event = TlsGetValue (g_cond_event_tls); - - if (!event) - { - win32_check_for_error (event = CreateEvent (0, FALSE, FALSE, NULL)); - TlsSetValue (g_cond_event_tls, event); - } - - EnterCriticalSection (&cond->lock); - - /* The event must not be signaled. Check this */ - g_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); - - g_ptr_array_add (cond->array, event); - LeaveCriticalSection (&cond->lock); - - g_mutex_unlock (entered_mutex); - - win32_check_for_error (WAIT_FAILED != - (retval = WaitForSingleObject (event, milliseconds))); - - g_mutex_lock (entered_mutex); - - if (retval == WAIT_TIMEOUT) - { - EnterCriticalSection (&cond->lock); - g_ptr_array_remove (cond->array, event); - - /* In the meantime we could have been signaled, so we must again - * wait for the signal, this time with no timeout, to reset - * it. retval is set again to honour the late arrival of the - * signal */ - win32_check_for_error (WAIT_FAILED != - (retval = WaitForSingleObject (event, 0))); - - LeaveCriticalSection (&cond->lock); - } - -#ifndef G_DISABLE_ASSERT - EnterCriticalSection (&cond->lock); - - /* Now event must not be inside the array, check this */ - g_assert (g_ptr_array_remove (cond->array, event) == FALSE); - - LeaveCriticalSection (&cond->lock); -#endif /* !G_DISABLE_ASSERT */ - - return retval != WAIT_TIMEOUT; -} - -static void -g_cond_wait_win32_impl (GCond *cond, - GMutex *entered_mutex) -{ - g_return_if_fail (cond != NULL); - g_return_if_fail (entered_mutex != NULL); - - g_cond_wait_internal (cond, entered_mutex, INFINITE); -} - -static gboolean -g_cond_timed_wait_win32_impl (GCond *cond, - GMutex *entered_mutex, - GTimeVal *abs_time) -{ - GTimeVal current_time; - gulong to_wait; - - g_return_val_if_fail (cond != NULL, FALSE); - g_return_val_if_fail (entered_mutex != NULL, FALSE); - - if (!abs_time) - to_wait = INFINITE; - else - { - g_get_current_time (¤t_time); - if (abs_time->tv_sec < current_time.tv_sec || - (abs_time->tv_sec == current_time.tv_sec && - abs_time->tv_usec <= current_time.tv_usec)) - to_wait = 0; - else - to_wait = (abs_time->tv_sec - current_time.tv_sec) * 1000 + - (abs_time->tv_usec - current_time.tv_usec) / 1000; - } - - return g_cond_wait_internal (cond, entered_mutex, to_wait); -} - -static void -g_cond_free_win32_impl (GCond * cond) -{ - DeleteCriticalSection (&cond->lock); - g_ptr_array_free (cond->array, TRUE); - g_free (cond); -} - static GPrivate * g_private_new_win32_impl (GDestroyNotify destructor) { @@ -325,6 +389,8 @@ g_private_get_win32_impl (GPrivate * private_key) return array[index]; } +/* {{{1 GThread */ + static void g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority) { @@ -385,7 +451,6 @@ g_thread_exit_win32_impl (void) GThreadData *self = TlsGetValue (g_thread_self_tls); guint i, private_max; gpointer *array = TlsGetValue (g_private_tls); - HANDLE event = TlsGetValue (g_cond_event_tls); EnterCriticalSection (&g_thread_global_spinlock); private_max = g_private_next; @@ -427,11 +492,8 @@ g_thread_exit_win32_impl (void) win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL)); } - if (event) - { - CloseHandle (event); - win32_check_for_error (TlsSetValue (g_cond_event_tls, NULL)); - } + if (g_thread_impl_vtable.CallThisOnThreadExit) + g_thread_impl_vtable.CallThisOnThreadExit (); _endthreadex (0); } @@ -513,19 +575,301 @@ g_thread_join_win32_impl (gpointer thread) g_free (target); } +/* {{{1 SRWLock and CONDITION_VARIABLE emulation (for Windows XP) */ + +static DWORD g_thread_xp_waiter_tls; +static CRITICAL_SECTION g_thread_xp_lock; + +/* {{{2 GThreadWaiter utility class for CONDITION_VARIABLE emulation */ +typedef struct _GThreadXpWaiter GThreadXpWaiter; +struct _GThreadXpWaiter +{ + HANDLE event; + volatile GThreadXpWaiter *next; +}; + +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"); + + TlsSetValue (g_thread_xp_waiter_tls, waiter); + } + + return waiter; +} + +static void +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 critical_section; +} GThreadSRWLock; + +static void +g_thread_xp_InitializeSRWLock (gpointer mutex) +{ + *(GThreadSRWLock * volatile *) mutex = NULL; +} + +static void +g_thread_xp_DeleteSRWLock (gpointer mutex) +{ + GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; + + if (lock) + { + DeleteCriticalSection (&lock->critical_section); + free (lock); + } +} + +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. + */ + result = *lock; + + if G_UNLIKELY (result == NULL) + { + EnterCriticalSection (&g_thread_xp_lock); + + result = malloc (sizeof (GThreadSRWLock)); + + if (result == NULL) + g_thread_abort (errno, "malloc"); + + InitializeCriticalSection (&result->critical_section); + *lock = result; + + LeaveCriticalSection (&g_thread_xp_lock); + } + + return result; +} + +static void +g_thread_xp_AcquireSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + EnterCriticalSection (&lock->critical_section); +} + +static BOOLEAN +g_thread_xp_TryAcquireSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = g_thread_xp_get_srwlock (mutex); + + return TryEnterCriticalSection (&lock->critical_section); +} + +static void +g_thread_xp_ReleaseSRWLockExclusive (gpointer mutex) +{ + GThreadSRWLock *lock = *(GThreadSRWLock * volatile *) mutex; + + /* 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); +} + +/* {{{2 CONDITION_VARIABLE emulation */ +typedef struct +{ + volatile GThreadXpWaiter *first; + volatile GThreadXpWaiter **last_ptr; +} GThreadXpCONDITION_VARIABLE; + +static void +g_thread_xp_InitializeConditionVariable (gpointer cond) +{ + *(GThreadXpCONDITION_VARIABLE * volatile *) cond = NULL; +} + +static void +g_thread_xp_DeleteConditionVariable (gpointer cond) +{ + GThreadXpCONDITION_VARIABLE *cv = *(GThreadXpCONDITION_VARIABLE * volatile *) cond; + + if (cv) + free (cv); +} + +static GThreadXpCONDITION_VARIABLE * +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 +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) +{ + 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); + + 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; + + EnterCriticalSection (&g_thread_xp_lock); + waiter = cv->first; + cv->first = NULL; + cv->last_ptr = &cv->first; + LeaveCriticalSection (&g_thread_xp_lock); + + while (waiter != NULL) + { + volatile GThreadXpWaiter *next; + + next = waiter->next; + SetEvent (waiter->event); + waiter = next; + } +} + +/* {{{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_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 */ + GThreadFunctions g_thread_functions_for_glib_use = { - g_mutex_new_win32_impl, /* mutex */ - g_mutex_lock_win32_impl, - g_mutex_trylock_win32_impl, - g_mutex_unlock_win32_impl, - g_mutex_free_win32_impl, - g_cond_new_win32_impl, /* condition */ - g_cond_signal_win32_impl, - g_cond_broadcast_win32_impl, - g_cond_wait_win32_impl, - g_cond_timed_wait_win32_impl, - g_cond_free_win32_impl, + 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, @@ -548,11 +892,56 @@ _g_thread_impl_init (void) beenhere = TRUE; + 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 ())); - win32_check_for_error (TLS_OUT_OF_INDEXES != - (g_cond_event_tls = TlsAlloc ())); InitializeCriticalSection (&g_thread_global_spinlock); } + +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(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_DllMain (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 +*/ + { + fprintf (stderr, "(debug) GThread using Windows XP mode\n"); + g_thread_xp_init (); + } +} + +/* vim:set foldmethod=marker: */ + diff --git a/glib/gthread.h b/glib/gthread.h index a6f380ec9..68d808d54 100644 --- a/glib/gthread.h +++ b/glib/gthread.h @@ -73,6 +73,37 @@ typedef struct _GCond GCond; typedef struct _GPrivate GPrivate; typedef struct _GStaticPrivate GStaticPrivate; +#ifdef G_OS_WIN32 + +#define G_MUTEX_INIT { NULL } +struct _GMutex +{ + gpointer impl; +}; + +#define G_COND_INIT { NULL } +struct _GCond +{ + gpointer impl; +}; +#else + +#include + +#define G_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER } +struct _GMutex +{ + pthread_mutex_t impl; +}; + +#define G_COND_INIT { PTHREAD_COND_INITIALIZER } +struct _GCond +{ + pthread_cond_t impl; +}; + +#endif + typedef struct _GThreadFunctions GThreadFunctions; struct _GThreadFunctions { @@ -153,49 +184,13 @@ GMutex* g_static_mutex_get_mutex_impl (GMutex **mutex); (*g_thread_functions_for_glib_use . op)) \ (mutex, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : (fail)) -#ifndef G_ERRORCHECK_MUTEXES -# define g_mutex_lock(mutex) \ - G_THREAD_CF (mutex_lock, (void)0, (mutex)) -# define g_mutex_trylock(mutex) \ - G_THREAD_CF (mutex_trylock, TRUE, (mutex)) -# define g_mutex_unlock(mutex) \ - G_THREAD_CF (mutex_unlock, (void)0, (mutex)) -# define g_mutex_free(mutex) \ - G_THREAD_CF (mutex_free, (void)0, (mutex)) -# define g_cond_wait(cond, mutex) \ - G_THREAD_CF (cond_wait, (void)0, (cond, mutex)) -# define g_cond_timed_wait(cond, mutex, abs_time) \ - G_THREAD_CF (cond_timed_wait, TRUE, (cond, mutex, abs_time)) -#else /* G_ERRORCHECK_MUTEXES */ -# define g_mutex_lock(mutex) \ - G_THREAD_ECF (mutex_lock, (void)0, (mutex), void) -# define g_mutex_trylock(mutex) \ - G_THREAD_ECF (mutex_trylock, TRUE, (mutex), gboolean) -# define g_mutex_unlock(mutex) \ - G_THREAD_ECF (mutex_unlock, (void)0, (mutex), void) -# define g_mutex_free(mutex) \ - G_THREAD_ECF (mutex_free, (void)0, (mutex), void) -# define g_cond_wait(cond, mutex) \ - (g_thread_supported () ? ((void(*)(GCond*, GMutex*, gulong, gchar*))\ - g_thread_functions_for_glib_use.cond_wait) \ - (cond, mutex, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : (void) 0) -# define g_cond_timed_wait(cond, mutex, abs_time) \ - (g_thread_supported () ? \ - ((gboolean(*)(GCond*, GMutex*, GTimeVal*, gulong, gchar*)) \ - g_thread_functions_for_glib_use.cond_timed_wait) \ - (cond, mutex, abs_time, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : TRUE) -#endif /* G_ERRORCHECK_MUTEXES */ + #if defined(G_THREADS_MANDATORY) #define g_thread_supported() 1 #else #define g_thread_supported() (g_threads_got_initialized) #endif -#define g_mutex_new() G_THREAD_UF (mutex_new, ()) -#define g_cond_new() G_THREAD_UF (cond_new, ()) -#define g_cond_signal(cond) G_THREAD_CF (cond_signal, (void)0, (cond)) -#define g_cond_broadcast(cond) G_THREAD_CF (cond_broadcast, (void)0, (cond)) -#define g_cond_free(cond) G_THREAD_CF (cond_free, (void)0, (cond)) #define g_private_new(destructor) G_THREAD_UF (private_new, (destructor)) #define g_private_get(private_key) G_THREAD_CF (private_get, \ ((gpointer)private_key), \ @@ -382,6 +377,33 @@ extern void glib_dummy_decl (void); # define G_TRYLOCK(name) g_static_mutex_trylock (&G_LOCK_NAME (name)) #endif /* !G_DEBUG_LOCKS */ + +void g_mutex_init (GMutex *mutex); +void g_mutex_clear (GMutex *mutex); + +void g_mutex_lock (GMutex *mutex); +void g_mutex_unlock (GMutex *mutex); +gboolean g_mutex_trylock (GMutex *mutex); + +void g_cond_init (GCond *cond); +void g_cond_clear (GCond *cond); + +void g_cond_wait (GCond *cond, + GMutex *mutex); +void g_cond_signal (GCond *cond); +void g_cond_broadcast (GCond *cond); +gboolean g_cond_timed_wait (GCond *cond, + GMutex *mutex, + GTimeVal *timeval); +gboolean g_cond_timedwait (GCond *cond, + GMutex *mutex, + gint64 abs_time); + +GMutex * g_mutex_new (void); +void g_mutex_free (GMutex *mutex); +GCond * g_cond_new (void); +void g_cond_free (GCond *cond); + G_END_DECLS #endif /* __G_THREAD_H__ */ diff --git a/glib/gutils.c b/glib/gutils.c index 162c2cf61..c7f2763b1 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -201,7 +201,10 @@ DllMain (HINSTANCE hinstDLL, LPVOID lpvReserved) { if (fdwReason == DLL_PROCESS_ATTACH) + { glib_dll = hinstDLL; + g_thread_DllMain (); + } return TRUE; }