diff options
author | Owen Taylor <otaylor@src.gnome.org> | 1998-12-15 05:28:02 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 1998-12-15 05:28:02 +0000 |
commit | 931ea952650b013b834041b91b0c37a748ffd449 (patch) | |
tree | 0c97c450b0e07953d836f1603c6fcb0357a58149 /gthread | |
parent | c8ba100dab8949c49097f11004c09ef36ea5136f (diff) |
This commit merges the glib-threads branch into the main
branch. See the ChangeLog for details of the changes.
In brief overview:
- The set of threading functions can be set
- A default implementation is provided in -lgthread
- All static data structures are locked using these
functions if g_thread_init() is called.
Diffstat (limited to 'gthread')
-rw-r--r-- | gthread/.cvsignore | 8 | ||||
-rw-r--r-- | gthread/Makefile.am | 21 | ||||
-rw-r--r-- | gthread/gthread-none.c | 28 | ||||
-rw-r--r-- | gthread/gthread-nspr.c | 218 | ||||
-rw-r--r-- | gthread/gthread-posix.c | 176 | ||||
-rw-r--r-- | gthread/gthread-solaris.c | 177 | ||||
-rw-r--r-- | gthread/gthread.c | 101 | ||||
-rw-r--r-- | gthread/testgthread.c | 200 |
8 files changed, 929 insertions, 0 deletions
diff --git a/gthread/.cvsignore b/gthread/.cvsignore new file mode 100644 index 000000000..ba4d80249 --- /dev/null +++ b/gthread/.cvsignore @@ -0,0 +1,8 @@ +Makefile.in +Makefile +.deps +*.lo +*.o +.libs +*.la +testgthread diff --git a/gthread/Makefile.am b/gthread/Makefile.am new file mode 100644 index 000000000..3b7bc9748 --- /dev/null +++ b/gthread/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gthread -DG_LOG_DOMAIN=g_log_domain_gthread + +EXTRA_DIST = \ + gthread-posix.c + +libglib = $(top_builddir)/libglib.la # -lglib + +lib_LTLIBRARIES = libgthread.la + +libgthread_la_SOURCES = gthread.c +libgthread_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) + +libgthread_la_LIBADD = \ + @G_THREAD_LIBS@ + +noinst_PROGRAMS = testgthread +testgthread_LDADD = ../libglib.la libgthread.la diff --git a/gthread/gthread-none.c b/gthread/gthread-none.c new file mode 100644 index 000000000..1240fd580 --- /dev/null +++ b/gthread/gthread-none.c @@ -0,0 +1,28 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: fallback thread system implementation + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* + * MT safe + */ + +static GThreadFunctions +g_mutex_functions_for_glib_use_default; /* is NULLified */ diff --git a/gthread/gthread-nspr.c b/gthread/gthread-nspr.c new file mode 100644 index 000000000..77672e5f0 --- /dev/null +++ b/gthread/gthread-nspr.c @@ -0,0 +1,218 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: nspr thread system implementation + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* + * MT safe + */ + +#include <prpdce.h> +#include <prthread.h> +#include <stdlib.h> + +#ifdef G_DISABLE_ASSERT + +#define STDERR_ASSERT(expr) + +#else /* G_DISABLE_ASSERT */ + +#define STDERR_ASSERT(expr) G_STMT_START{ \ + if (!(expr)) \ + g_log (G_LOG_DOMAIN, \ + G_LOG_LEVEL_ERROR, \ + "file %s: line %d: assertion failed: (%s)", \ + __FILE__, \ + __LINE__, \ + #expr); }G_STMT_END + +#endif /* G_DISABLE_ASSERT */ + +/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use + functions from gmem.c and gmessages.c; */ + +static gboolean +g_mutex_trylock_nspr_impl (GMutex * mutex) +{ + PRStatus status = PRP_TryLock ((PRLock *) mutex); + if (status == PR_SUCCESS) + { + return TRUE; + } + return FALSE; +} + +static void +g_cond_wait_nspr_impl (GCond * cond, + GMutex * entered_mutex) +{ + PRStatus status = PRP_NakedWait ((PRCondVar *) cond, + (PRLock *) entered_mutex, + PR_INTERVAL_NO_TIMEOUT); + g_assert (status == PR_SUCCESS); +} + +#define G_MICROSEC 1000000 + +static gboolean +g_cond_timed_wait_nspr_impl (GCond * cond, + GMutex * entered_mutex, + GTimeVal * abs_time) +{ + PRStatus status; + PRIntervalTime interval; + GTimeVal current_time; + glong microsecs; + + g_return_val_if_fail (cond != NULL, FALSE); + g_return_val_if_fail (entered_mutex != NULL, FALSE); + + 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)) + return FALSE; + + interval = PR_SecondsToInterval (abs_time->tv_sec - current_time.tv_sec); + microsecs = abs_time->tv_usec - current_time.tv_usec; + if (microsecs < 0) + interval -= PR_MicrosecondsToInterval (-microsecs); + else + interval += PR_MicrosecondsToInterval (microsecs); + + status = PRP_NakedWait ((PRCondVar *) cond, (PRLock *) entered_mutex, + interval); + + g_assert (status == PR_SUCCESS); + + 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)) + return FALSE; + return TRUE; +} + +typedef struct _GPrivateNSPRData GPrivateNSPRData; +struct _GPrivateNSPRData + { + gpointer data; + GDestroyNotify destructor; + }; + +typedef struct _GPrivateNSPR GPrivateNSPR; +struct _GPrivateNSPR + { + PRUintn private; + GDestroyNotify destructor; + }; + +static GPrivateNSPRData * +g_private_nspr_data_constructor (GDestroyNotify destructor, gpointer data) +{ + /* we can not use g_new and friends, as they might use private data by + themself */ + GPrivateNSPRData *private = malloc (sizeof (GPrivateNSPRData)); + g_assert (private); + private->data = data; + private->destructor = destructor; + + return private; +} + +static void +g_private_nspr_data_destructor (gpointer data) +{ + GPrivateNSPRData *private = data; + if (private->destructor && private->data) + (*private->destructor) (private->data); + free (private); +} + +static GPrivate * +g_private_new_nspr_impl (GDestroyNotify destructor) +{ + GPrivateNSPR *result = g_new (GPrivateNSPR, 1); + PRStatus status = PR_NewThreadPrivateIndex (&result->private, + g_private_nspr_data_destructor); + g_assert (status == PR_SUCCESS); + + result->destructor = destructor; + return (GPrivate *) result; +} + +/* NOTE: the functions g_private_get and g_private_set may not use + functions from gmem.c and gmessages.c */ + +static GPrivateNSPRData * +g_private_nspr_data_get (GPrivateNSPR * private) +{ + GPrivateNSPRData *data; + + STDERR_ASSERT (private); + + data = PR_GetThreadPrivate (private->private); + if (!data) + { + data = g_private_nspr_data_constructor (private->destructor, NULL); + STDERR_ASSERT (PR_SetThreadPrivate (private->private, data) + == PR_SUCCESS); + } + + return data; +} + +static void +g_private_set_nspr_impl (GPrivate * private, gpointer value) +{ + if (!private) + return; + + g_private_nspr_data_get ((GPrivateNSPR *) private)->data = value; +} + +static gpointer +g_private_get_nspr_impl (GPrivate * private) +{ + if (!private) + return NULL; + + return g_private_nspr_data_get ((GPrivateNSPR *) private)->data; +} + +static GThreadFunctions g_thread_functions_for_glib_use_default = +{ + (GMutex * (*)())PR_NewLock, + (void (*)(GMutex *)) PR_Lock, + g_mutex_trylock_nspr_impl, + (void (*)(GMutex *)) PR_Unlock, + (void (*)(GMutex *)) PR_DestroyLock, + (GCond * (*)())PRP_NewNakedCondVar, + (void (*)(GCond *)) PRP_NakedNotify, + (void (*)(GCond *)) PRP_NakedBroadcast, + g_cond_wait_nspr_impl, + g_cond_timed_wait_nspr_impl, + (void (*)(GCond *)) PRP_DestroyNakedCondVar, + g_private_new_nspr_impl, + g_private_get_nspr_impl, + g_private_set_nspr_impl +}; diff --git a/gthread/gthread-posix.c b/gthread/gthread-posix.c new file mode 100644 index 000000000..5b59672fe --- /dev/null +++ b/gthread/gthread-posix.c @@ -0,0 +1,176 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: posix thread system implementation + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* + * MT safe + */ + +#include <pthread.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/time.h> + +#define posix_print_error( name, num ) \ + g_error( "file %s: line %d (%s): error %s during %s", \ + __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \ + g_strerror((num)), #name ) + +#define posix_check_for_error( what ) G_STMT_START{ \ + int error = (what); \ + if( error ) { posix_print_error( what, error ); } \ + }G_STMT_END + +static GMutex * +g_mutex_new_posix_impl (void) +{ + GMutex *result = (GMutex *) g_new (pthread_mutex_t, 1); + posix_check_for_error (pthread_mutex_init ((pthread_mutex_t *) result, NULL)); + return result; +} + +static void +g_mutex_free_posix_impl (GMutex * mutex) +{ + posix_check_for_error (pthread_mutex_destroy ((pthread_mutex_t *) mutex)); + 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_for_error (result); + return TRUE; +} + +static GCond * +g_cond_new_posix_impl (void) +{ + GCond *result = (GCond *) g_new (pthread_cond_t, 1); + posix_check_for_error (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 + therfore. */ + +#define G_MICROSEC 1000000 +#define G_NANOSEC 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) + { + g_cond_wait (cond, entered_mutex); + return TRUE; + } + + end_time.tv_sec = abs_time->tv_sec; + end_time.tv_nsec = abs_time->tv_usec * (G_NANOSEC / G_MICROSEC); + g_assert (end_time.tv_nsec < G_NANOSEC); + result = pthread_cond_timedwait ((pthread_cond_t *) cond, + (pthread_mutex_t *) entered_mutex, + &end_time); + timed_out = (result == ETIMEDOUT); + if (!timed_out) + posix_check_for_error (result); + return !timed_out; +} + +static void +g_cond_free_posix_impl (GCond * cond) +{ + posix_check_for_error (pthread_cond_destroy ((pthread_cond_t *) cond)); + g_free (cond); +} + +static GPrivate * +g_private_new_posix_impl (GDestroyNotify destructor) +{ + GPrivate *result = (GPrivate *) g_new (pthread_key_t, 1); + posix_check_for_error (pthread_key_create ((pthread_key_t *) result, + destructor)); + 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_posix_impl (GPrivate * private, gpointer value) +{ + if (!private) + return; + + pthread_setspecific (*(pthread_key_t *) private, value); +} + +static gpointer +g_private_get_posix_impl (GPrivate * private) +{ + if (!private) + return NULL; + + return pthread_getspecific (*(pthread_key_t *) private); +} + +static GThreadFunctions g_thread_functions_for_glib_use_default = +{ + 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_private_new_posix_impl, + g_private_get_posix_impl, + g_private_set_posix_impl +}; diff --git a/gthread/gthread-solaris.c b/gthread/gthread-solaris.c new file mode 100644 index 000000000..52d4c5552 --- /dev/null +++ b/gthread/gthread-solaris.c @@ -0,0 +1,177 @@ +/* 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 Sebastian Wilhelmi; University of Karlsruhe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* + * MT safe + */ + +#include <thread.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#define solaris_print_error( name, num ) \ + g_error( "file %s: line %d (%s): error %s during %s", \ + __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \ + g_strerror((num)), #name ) + +#define solaris_check_for_error( what ) G_STMT_START{ \ + int error = (what); \ + if( error ) { solaris_print_error( what, error ); } \ + }G_STMT_END + +static GMutex * +g_mutex_new_solaris_impl (void) +{ + GMutex *result = (GMutex *) g_new (mutex_t, 1); + solaris_check_for_error (mutex_init ((mutex_t *) result, USYNC_PROCESS, 0)); + return result; +} + +static void +g_mutex_free_solaris_impl (GMutex * mutex) +{ + solaris_check_for_error (mutex_destroy ((mutex_t *) mutex)); + free (mutex); +} + +/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use + functions from gmem.c and gmessages.c; */ + +/* mutex_lock, 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_solaris_impl (GMutex * mutex) +{ + int result; + result = mutex_trylock ((mutex_t *) mutex); + if (result == EBUSY) + return FALSE; + solaris_check_for_error (result); + return TRUE; +} + +static GCond * +g_cond_new_solaris_impl () +{ + GCond *result = (GCond *) g_new (cond_t, 1); + solaris_check_for_error (cond_init ((cond_t *) result, USYNC_THREAD, 0)); + return result; +} + +/* cond_signal, cond_broadcast and cond_wait + can be taken directly, as signature and semantic are right, but + without error check then!!!!, we might want to change this + therfore. */ + +#define G_MICROSEC 1000000 +#define G_NANOSEC 1000000000 + +static gboolean +g_cond_timed_wait_solaris_impl (GCond * cond, + GMutex * entered_mutex, + GTimeVal * abs_time) +{ + int result; + timestruc_t 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) + { + g_cond_wait (cond, entered_mutex); + return TRUE; + } + + end_time.tv_sec = abs_time->tv_sec; + end_time.tv_nsec = abs_time->tv_usec * (G_NANOSEC / G_MICROSEC); + g_assert (end_time.tv_nsec < G_NANOSEC); + result = cond_timedwait ((cond_t *) cond, (mutex_t *) entered_mutex, + &end_time); + timed_out = (result == ETIME); + if (!timed_out) + solaris_check_for_error (result); + return !timed_out; +} + +static void +g_cond_free_solaris_impl (GCond * cond) +{ + solaris_check_for_error (cond_destroy ((cond_t *) cond)); + g_free (cond); +} + +static GPrivate * +g_private_new_solaris_impl (GDestroyNotify destructor) +{ + GPrivate *result = (GPrivate *) g_new (thread_key_t,1); + solaris_check_for_error (thr_keycreate ((thread_key_t *) result, + destructor)); + 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_solaris_impl (GPrivate * private, gpointer value) +{ + if (!private) + return; + + thr_setspecific (*(thread_key_t *) private, value); +} + +static gpointer +g_private_get_solaris_impl (GPrivate * private) +{ + gpointer result; + + if (!private) + return NULL; + + thr_getspecific (*(thread_key_t *) private, &result); + + return result; +} + +static GThreadFunctions g_thread_functions_for_glib_use_default = +{ + g_mutex_new_solaris_impl, + (void (*)(GMutex *)) mutex_lock, + g_mutex_trylock_solaris_impl, + (void (*)(GMutex *)) mutex_unlock, + g_mutex_free_solaris_impl, + g_cond_new_solaris_impl, + (void (*)(GCond *)) cond_signal, + (void (*)(GCond *)) cond_broadcast, + (void (*)(GCond *, GMutex *)) cond_wait, + g_cond_timed_wait_solaris_impl, + g_cond_free_solaris_impl, + g_private_new_solaris_impl, + g_private_get_solaris_impl, + g_private_set_solaris_impl +}; diff --git a/gthread/gthread.c b/gthread/gthread.c new file mode 100644 index 000000000..8ada7f89d --- /dev/null +++ b/gthread/gthread.c @@ -0,0 +1,101 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gthread.c: thread related functions + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* + * MT safe + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> + +static const char *g_log_domain_gthread = "GThread"; +static gboolean thread_system_already_initialized = FALSE; + +#include G_THREAD_SOURCE + +void g_mutex_init (void); +void g_mem_init (void); +void g_messages_init (void); + +void +g_thread_init(GThreadFunctions* init) +{ + gboolean supported; + + if (thread_system_already_initialized) + g_error ("the glib thread system may only be initialized once."); + + thread_system_already_initialized = TRUE; + + if (init == NULL) + init = &g_thread_functions_for_glib_use_default; + else + g_thread_use_default_impl = FALSE; + + g_thread_functions_for_glib_use = *init; + + /* It is important, that g_thread_supported is not set before the + thread initialization functions of the different modules are + called */ + + supported = + init->mutex_new && + init->mutex_lock && + init->mutex_trylock && + init->mutex_unlock && + init->mutex_free && + init->cond_new && + init->cond_signal && + init->cond_broadcast && + init->cond_wait && + init->cond_timed_wait && + init->cond_free && + init->private_new && + init->private_get && + init->private_get; + + /* if somebody is calling g_thread_init (), it means that he wants to + have thread support, so check this */ + + if (!supported) + { + if (g_thread_use_default_impl) + g_error ("Threads are not supported on this platform."); + else + g_error ("The supplied thread function vector is invalid."); + } + + /* now call the thread initialization functions of the different + glib modules. BTW: order does matter, g_mutex_init MUST be first */ + + g_mutex_init (); + g_mem_init (); + g_messages_init (); + + /* now we can set g_thread_supported and thus enable all the thread + functions */ + + g_thread_supported = TRUE; +} diff --git a/gthread/testgthread.c b/gthread/testgthread.c new file mode 100644 index 000000000..be81f0490 --- /dev/null +++ b/gthread/testgthread.c @@ -0,0 +1,200 @@ +#include <stdlib.h> + +#define main testglib_main +#include <testglib.c> +#undef main + +#define TEST_PRIVATE_THREADS 9 +#define TEST_PRIVATE_ROUNDS 5 + +void +test_mutexes () +{ + GMutex *mutex = NULL; + GCond *cond = NULL; + GStaticMutex static_mutex = G_STATIC_MUTEX_INIT; + G_LOCK_DEFINE (test_me); + + if (g_thread_supported) + { + mutex = g_mutex_new (); + cond = g_cond_new (); + } + + g_mutex_lock (mutex); + g_mutex_unlock (mutex); + + g_static_mutex_lock (static_mutex); + g_static_mutex_unlock (static_mutex); + + g_cond_signal (cond); + g_cond_broadcast (cond); + + g_lock (test_me); + g_unlock (test_me); + + if (g_thread_supported) + { + g_cond_free (cond); + g_mutex_free (mutex); + } +} + +#if defined(NSPR) /* we are using nspr threads */ +/* this option must be specified by hand during compile of + testgthread. also note, that you have to link with whatever library + nspr is building upon, it might otherwise (as on solaris) lead to + run time failure, as the mutex functions are defined in libc, but + as noops, that will make some nspr assertions fail. */ +#include <prthread.h> + +gpointer +new_thread (GHookFunc func, gpointer data) +{ + PRThread *thread = PR_CreateThread (PR_SYSTEM_THREAD, func, data, + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, 0); + return thread; +} +#define join_thread(thread) PR_JoinThread (thread) +#define self_thread() PR_GetCurrentThread () + +#elif defined(DEFAULTMUTEX) /* we are using solaris threads */ + +gpointer +new_thread (GHookFunc func, gpointer data) +{ + thread_t thread; + thr_create (NULL, 0, (void *(*)(void *)) func, data, THR_BOUND, &thread); + return GUINT_TO_POINTER (thread); +} +#define join_thread(thread) \ + thr_join ((thread_t)GPOINTER_TO_UINT (thread), NULL, NULL) +#define self_thread() GUINT_TO_POINTER (thr_self ()) + +#elif defined(PTHREAD_MUTEX_INITIALIZER) /* we are using posix threads */ +gpointer +new_thread(GHookFunc func, gpointer data) +{ + pthread_t thread; + pthread_attr_t pthread_attr; + pthread_attr_init (&pthread_attr); + pthread_attr_setdetachstate (&pthread_attr, PTHREAD_CREATE_JOINABLE); + pthread_create (&thread, &pthread_attr, (void *(*)(void *)) func, data); + return GUINT_TO_POINTER (thread); +} +#define join_thread(thread) \ + pthread_join ((pthread_t)GPOINTER_TO_UINT (thread), NULL) +#define self_thread() GUINT_TO_POINTER (pthread_self ()) + +#else /* we are not having a thread implementation, do nothing */ + +#define new_thread(func,data) (NULL) +#define join_thread(thread) ((void)0) +#define self_thread() NULL + +#endif + +#define G_MICROSEC 1000000 + +void +wait_thread (double seconds) +{ + GMutex *mutex; + GCond *cond; + GTimeVal current_time; + + g_get_current_time (¤t_time); + mutex = g_mutex_new (); + cond = g_cond_new (); + + current_time.tv_sec += (guint) seconds; + seconds -= (guint) seconds; + current_time.tv_usec += (guint) (seconds * G_MICROSEC); + while (current_time.tv_usec >= G_MICROSEC) + { + current_time.tv_usec -= G_MICROSEC; + current_time.tv_sec++; + } + + g_mutex_lock (mutex); + g_cond_timed_wait (cond, mutex, ¤t_time); + g_mutex_unlock (mutex); + + g_mutex_free (mutex); + g_cond_free (cond); +} + +gpointer +private_constructor () +{ + gpointer *result = g_new (gpointer, 2); + result[0] = 0; + result[1] = self_thread (); + g_print ("allocating data for the thread %p.\n", result[1]); + return result; +} + +void +private_destructor (gpointer data) +{ + gpointer *real = data; + g_print ("freeing data for the thread %p.\n", real[1]); + g_free (real); +} + +GStaticPrivate private; + +void +test_private_func (void *data) +{ + guint i = 0; + wait_thread (1); + while (i < TEST_PRIVATE_ROUNDS) + { + guint random_value = rand () % 10000; + guint *data = g_static_private_get (&private); + if (!data) + { + data = private_constructor (); + g_static_private_set (&private, data, private_destructor); + } + *data = random_value; + wait_thread (.2); + g_assert (*(guint *) g_static_private_get (&private) == random_value); + i++; + } +} + +void +test_private () +{ + int i; + gpointer threads[TEST_PRIVATE_THREADS]; + for (i = 0; i < TEST_PRIVATE_THREADS; i++) + { + threads[i] = new_thread (test_private_func, (gpointer) i); + } + for (i = 0; i < TEST_PRIVATE_THREADS; i++) + { + join_thread (threads[i]); + } + g_print ("\n"); +} + +int +main () +{ + test_mutexes (); + + g_thread_init (NULL); + + test_mutexes (); + + test_private (); + + /* later we might want to start n copies of that */ + testglib_main (0, NULL); + + return 0; +} |