diff options
author | Sebastian Wilhelmi <wilhelmi@ira.uka.de> | 2001-05-22 12:28:06 +0000 |
---|---|---|
committer | Sebastian Wilhelmi <wilhelmi@src.gnome.org> | 2001-05-22 12:28:06 +0000 |
commit | d6ed8e36e78373329315d743743c7480724e87ef (patch) | |
tree | 05de724e8158c5bef3d0e1c2c7a4ece86d01a0c0 /gthread/gthread-win32.c | |
parent | 673e99b7f30ce516f213e97fa78522493242335a (diff) | |
download | glib-d6ed8e36e78373329315d743743c7480724e87ef.tar.gz |
Removed POSIX_*. Defined G_THREAD_SOURCE to "gthread-win32.c".
2001-05-22 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* config.h.win32.in: Removed POSIX_*. Defined G_THREAD_SOURCE to
"gthread-win32.c".
* glibconfig.h.win32.in: Define G_HAVE_ISO_VARARGS for gcc, don't
know about MSC. Define G_THREADS_IMPL_WIN32 instead of
G_THREADS_IMPL_POSIX and define the right static mutex macros and
types.
* build/win32/make.mingw (CXX): Removed PTHREAD defs. Added
-O2 -Wall to compile flags.
* gthread/gthread-impl.c (g_thread_init): Move the thread
implementation initialization to before assigning
GThreadFuncs, which now is just struct assigned and not
memcpy'ed. Completed check for zero members of GThreadFuncs.
* gthread/makefile.mingw: Don't link to pthread anymore.
* gthread/gthread-win32.c: New file for native thread support for
win32. Thanks to Hans Breuer <hans@breuer.org> to got me
kickstarted.
* gthread/Makefile.am: Also distribute gthread-win32.c.
Diffstat (limited to 'gthread/gthread-win32.c')
-rw-r--r-- | gthread/gthread-win32.c | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/gthread/gthread-win32.c b/gthread/gthread-win32.c new file mode 100644 index 000000000..17b9eea52 --- /dev/null +++ b/gthread/gthread-win32.c @@ -0,0 +1,567 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: solaris thread system implementation + * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe + * Copyright 2001 Hans Breuer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * MT safe + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> + +#define STRICT +#include <windows.h> +#undef STRICT + +#include <process.h> +#include <malloc.h> + +#define win32_check_for_error(what) G_STMT_START{ \ + if (!(what)) \ + g_error ("file %s: line %d (%s): error %ld during %s", \ + __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \ + GetLastError (), #what); \ + }G_STMT_END + +#define G_MUTEX_SIZE (sizeof (HANDLE)) + +#define PRIORITY_LOW_VALUE THREAD_PRIORITY_BELOW_NORMAL +#define PRIORITY_NORMAL_VALUE THREAD_PRIORITY_NORMAL +#define PRIORITY_HIGH_VALUE THREAD_PRIORITY_ABOVE_NORMAL +#define PRIORITY_URGENT_VALUE THREAD_PRIORITY_HIGHEST + +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 *); + +static GTryEnterCriticalSectionFunc try_enter_critical_section = NULL; + +/* 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 16 + +static GDestroyNotify g_private_destructors[G_PRIVATE_MAX]; + +static guint g_private_next = 0; + +typedef struct _GThreadData GThreadData; +struct _GThreadData +{ + GThreadFunc func; + gpointer data; + HANDLE thread; + gboolean joinable; +}; + +struct _GCond +{ + GPtrArray *array; + CRITICAL_SECTION lock; +}; + +static GMutex * +g_mutex_new_win32_cs_impl (void) +{ + CRITICAL_SECTION *retval = g_new (CRITICAL_SECTION, 1); + InitializeCriticalSection (retval); + return (GMutex *) retval; +} + +static void +g_mutex_free_win32_cs_impl (GMutex *mutex) +{ + 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_cs_impl (GMutex *mutex) +{ + EnterCriticalSection ((CRITICAL_SECTION *)mutex); +} + +static gboolean +g_mutex_trylock_win32_cs_impl (GMutex * mutex) +{ + return try_enter_critical_section ((CRITICAL_SECTION *)mutex); +} + +static void +g_mutex_unlock_win32_cs_impl (GMutex *mutex) +{ + LeaveCriticalSection ((CRITICAL_SECTION *)mutex); +} + +static GMutex * +g_mutex_new_win32_impl (void) +{ + HANDLE handle; + win32_check_for_error (handle = CreateMutex (NULL, FALSE, NULL)); + return (GMutex *) handle; +} + +static void +g_mutex_free_win32_impl (GMutex *mutex) +{ + win32_check_for_error (CloseHandle ((HANDLE) 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) +{ + WaitForSingleObject ((HANDLE) mutex, INFINITE); +} + +static gboolean +g_mutex_trylock_win32_impl (GMutex * mutex) +{ + DWORD result; + win32_check_for_error (WAIT_FAILED != + (result = WaitForSingleObject ((HANDLE)mutex, 0))); + return result != WAIT_TIMEOUT; +} + +static void +g_mutex_unlock_win32_impl (GMutex *mutex) +{ + ReleaseMutex ((HANDLE) 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 */ + win32_check_for_error (WAIT_FAILED != 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); + + g_get_current_time (¤t_time); + 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) +{ + g_ptr_array_free (cond->array, TRUE); + g_free (cond); +} + +static GPrivate * +g_private_new_win32_impl (GDestroyNotify destructor) +{ + GPrivate *result; + EnterCriticalSection (&g_thread_global_spinlock); + if (g_private_next >= G_PRIVATE_MAX) + g_error ("Too many GPrivate allocated. Their number is limited to %d.\n" + "Use GStaticPrivate instead.\n", G_PRIVATE_MAX); + g_private_destructors[g_private_next] = destructor; + result = GUINT_TO_POINTER (g_private_next); + g_private_next++; + LeaveCriticalSection (&g_thread_global_spinlock); + + return result; +} + +/* 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) +{ + 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; +} + +static gpointer +g_private_get_win32_impl (GPrivate * private_key) +{ + gpointer* array = TlsGetValue (g_private_tls); + guint index = GPOINTER_TO_UINT (private_key); + + if (index >= G_PRIVATE_MAX || !array) + return NULL; + + return array[index]; +} + +static void +g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority) +{ + GThreadData *target = *(GThreadData **)thread; + + g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW); + g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT); + + win32_check_for_error (SetThreadPriority (target->thread, + g_thread_priority_map [priority])); +} + +static void +g_thread_self_win32_impl (gpointer thread) +{ + 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; +} + +static void +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; + LeaveCriticalSection (&g_thread_global_spinlock); + + if (array) + { + for (i = 0; i < private_max; i++) + { + GDestroyNotify destructor = g_private_destructors[i]; + GDestroyNotify data = array[i]; + if (destructor && data) + destructor (data); + } + + g_free (array); + + win32_check_for_error (TlsSetValue (g_private_tls, NULL)); + } + + if (self) + { + if (!self->joinable) + { + win32_check_for_error (CloseHandle (self->thread)); + g_free (self); + } + win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL)); + } + + if (event) + { + CloseHandle (event); + win32_check_for_error (TlsSetValue (g_cond_event_tls, NULL)); + } + + _endthreadex (0); +} + +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 (); + + return 0; +} + +static void +g_thread_create_win32_impl (GThreadFunc func, + gpointer data, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority, + gpointer thread, + GError **error) +{ + 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; + + retval->joinable = joinable; + + retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy, + retval, 0, &ignore); + + if (retval->thread == NULL) + { + g_free (retval); + g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, + "Error creating thread: %ld", GetLastError()); + return; + } + + *(GThreadData **)thread = retval; + + g_thread_set_priority_win32_impl (thread, priority); +} + +static void +g_thread_yield_win32_impl (void) +{ + Sleep(0); +} + +static void +g_thread_join_win32_impl (gpointer thread) +{ + GThreadData *target = *(GThreadData **)thread; + + g_return_if_fail (target->joinable); + + win32_check_for_error (WAIT_FAILED != + WaitForSingleObject (target->thread, INFINITE)); + + win32_check_for_error (CloseHandle (target->thread)); + g_free (target); +} + +static GThreadFunctions g_thread_functions_for_glib_use_default = +{ + 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_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 +}; + +#define HAVE_G_THREAD_IMPL_INIT +static void +g_thread_impl_init () +{ + HMODULE kernel32; + + 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); + + /* Here we are looking for TryEnterCriticalSection in KERNEL32.DLL, + * if it is found, we can use the faster critical sections instead + * of mutexes. Note however that + * http://www2.awl.com/cseng/titles/0-201-63465-1/csmutx.htm indicates, + * that critical sections might not be ideal after all on SMP machines */ + kernel32 = GetModuleHandle ("KERNEL32.DLL"); + if (kernel32) + { + try_enter_critical_section = (GTryEnterCriticalSectionFunc) + GetProcAddress(kernel32, "TryEnterCriticalSection"); + + /* Even if TryEnterCriticalSection is found, it is not + * necessarily working..., we have to check it */ + if (try_enter_critical_section && + try_enter_critical_section (&g_thread_global_spinlock)) + { + LeaveCriticalSection (&g_thread_global_spinlock); + + g_thread_functions_for_glib_use_default.mutex_new = + g_mutex_new_win32_cs_impl; + g_thread_functions_for_glib_use_default.mutex_lock = + g_mutex_lock_win32_cs_impl; + g_thread_functions_for_glib_use_default.mutex_trylock = + g_mutex_trylock_win32_cs_impl; + g_thread_functions_for_glib_use_default.mutex_unlock = + g_mutex_unlock_win32_cs_impl; + g_thread_functions_for_glib_use_default.mutex_free = + g_mutex_free_win32_cs_impl; + } + } +} + |