summaryrefslogtreecommitdiff
path: root/gthread
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@src.gnome.org>1998-12-15 05:28:02 +0000
committerOwen Taylor <otaylor@src.gnome.org>1998-12-15 05:28:02 +0000
commit931ea952650b013b834041b91b0c37a748ffd449 (patch)
tree0c97c450b0e07953d836f1603c6fcb0357a58149 /gthread
parentc8ba100dab8949c49097f11004c09ef36ea5136f (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/.cvsignore8
-rw-r--r--gthread/Makefile.am21
-rw-r--r--gthread/gthread-none.c28
-rw-r--r--gthread/gthread-nspr.c218
-rw-r--r--gthread/gthread-posix.c176
-rw-r--r--gthread/gthread-solaris.c177
-rw-r--r--gthread/gthread.c101
-rw-r--r--gthread/testgthread.c200
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 (&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))
+ 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 (&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))
+ 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 (&current_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, &current_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;
+}