Rework GMutex and GCond APIs

Do a substantial rework of the GMutex and GCond APIs.

 - remove all of the macro indirection hackery which is no longer needed
   since we dropped support for switchable thread implementations

 - expose the structure types and add G_MUTEX_INIT and G_COND_INIT
   static initialiser macros

 - add g_mutex_init() and g_mutex_clear() for use when embedding GMutex
   into another structure type and do the same for GCond as well

 - avoid infinite recursion hazards by ensuring that neither GCond or
   GMutex ever calls back into any other part of GLib

 - substantially rework the Windows implementation of GCond and GMutex
   to use the SRWLock and CONDITION_VARIABLE APIs present on Windows
   2008/Vista and later, emulating these APIs on XP
This commit is contained in:
Ryan Lortie 2011-09-16 18:05:23 -04:00
parent c6f84faa17
commit 80730bc75c
5 changed files with 937 additions and 360 deletions

View File

@ -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

View File

@ -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 <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
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: */

View File

@ -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 <windows.h>
#undef STRICT
#include <process.h>
#include <stdlib.h>
#include <stdio.h>
static void
g_thread_abort (gint status,
const gchar *function)
{
fprintf (stderr, "GLib (gthread-win32.c): Unexpected error from C library during '%s': %s. Aborting.\n",
strerror (status), function);
abort ();
}
/* Starting with Vista and Windows 2008, we have access to the
* CONDITION_VARIABLE and SRWLock primatives on Windows, which are
* pretty reasonable approximations of the primatives specified in
* POSIX 2001 (pthread_cond_t and pthread_mutex_t respectively).
*
* Both of these types are structs containing a single pointer. That
* pointer is used as an atomic bitfield to support user-space mutexes
* that only get the kernel involved in cases of contention (similar
* to how futex()-based mutexes work on Linux). The biggest advantage
* of these new types is that they can be statically initialised to
* zero. 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 (&current_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: */

View File

@ -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 <pthread.h>
#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__ */

View File

@ -201,7 +201,10 @@ DllMain (HINSTANCE hinstDLL,
LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
glib_dll = hinstDLL;
g_thread_DllMain ();
}
return TRUE;
}