From 90f6cc9bf2453e5da385b2d547704091dd8afbb8 Mon Sep 17 00:00:00 2001 From: Sebastian Wilhelmi Date: Thu, 17 Jun 1999 15:39:31 +0000 Subject: [PATCH] Completed the thread support in GLib. Thread creation, prioritizing 1999-06-17 Sebastian Wilhelmi * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: Completed the thread support in GLib. Thread creation, prioritizing threads, yielding, joining threads as well as reader/writer locks and recursive mutexes are now in place. Please test heavily on your platform. It is so far tested on Linux/i386/pthreads, Solaris/Sparc/pthreads and Solaris/Sparc/solaristhreads. * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for thread safe sleeping. (sleep() is not MT-safe at all!) * gutils.c: Avoid compiler warning. * tests/Makefile.am, tests/thread-test.c: New program to test some aspects of the thread implementation. * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the change of content. * configure.in: Purged all appearances of nspr. * gthread/gthread-posix.c, gthread-solaris.c: Added the native implementations for the GLib's extended thread support. * gthread/gthread-nspr.c: Removed for good. NSPR is nothing we would want to build upon. * gthread/gthread.c: Renamed to gthread-impl.c to avoid confusion with ../gthread.c (Formerly known as the file called gmutex.c) * gthread/testgthread.c: Removed. The new and much extended tests are in ../tests/thread-test.c. * gthread/Makefile.am: Changed to reflect the changes above. --- ChangeLog | 23 ++ ChangeLog.pre-2-0 | 23 ++ ChangeLog.pre-2-10 | 23 ++ ChangeLog.pre-2-12 | 23 ++ ChangeLog.pre-2-2 | 23 ++ ChangeLog.pre-2-4 | 23 ++ ChangeLog.pre-2-6 | 23 ++ ChangeLog.pre-2-8 | 23 ++ Makefile.am | 2 +- acconfig.h | 6 + acglib.m4 | 4 +- configure.in | 118 +++++++- glib.h | 134 +++++++-- glib/Makefile.am | 2 +- glib/glib.h | 134 +++++++-- glib/gthread.c | 535 ++++++++++++++++++++++++++++++++++ glib/gtimer.c | 12 +- glib/gutils.c | 4 +- gmutex.c | 202 ------------- gthread.c | 535 ++++++++++++++++++++++++++++++++++ gthread/ChangeLog | 16 + gthread/Makefile.am | 6 +- gthread/{gthread.c => gthread-impl.c} | 30 ++ gthread/gthread-nspr.c | 225 -------------- gthread/gthread-posix.c | 122 +++++++- gthread/gthread-solaris.c | 64 +++- gthread/testgthread.c | 217 -------------- gtimer.c | 12 +- gutils.c | 4 +- tests/Makefile.am | 5 +- tests/thread-test.c | 238 +++++++++++++++ 31 files changed, 2095 insertions(+), 716 deletions(-) create mode 100644 glib/gthread.c delete mode 100644 gmutex.c create mode 100644 gthread.c rename gthread/{gthread.c => gthread-impl.c} (76%) delete mode 100644 gthread/gthread-nspr.c delete mode 100644 gthread/testgthread.c create mode 100644 tests/thread-test.c diff --git a/ChangeLog b/ChangeLog index 79d4c3d..1883290 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 79d4c3d..1883290 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,26 @@ +1999-06-17 Sebastian Wilhelmi + + * configure.in, acglib.m4, acconfig.h, glib.h, gthread.c: + Completed the thread support in GLib. Thread creation, + prioritizing threads, yielding, joining threads as well as + reader/writer locks and recursive mutexes are now in place. Please + test heavily on your platform. It is so far tested on + Linux/i386/pthreads, Solaris/Sparc/pthreads and + Solaris/Sparc/solaristhreads. + + * gtimer.c, glib.h: Implement g_usleep (gulong microseconds) for + thread safe sleeping. (sleep() is not MT-safe at all!) + + * gutils.c: Avoid compiler warning. + + * tests/Makefile.am, tests/thread-test.c: New program to test some + aspects of the thread implementation. + + * gthread.c, Makefile.am: Renamed from gmutex.c to reflect the + change of content. + + * configure.in: Purged all appearances of nspr. + Wed Jun 2 11:42:46 PDT 1999 Manish Singh * acinclude.m4 diff --git a/Makefile.am b/Makefile.am index 0725691..f5a8c47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,6 @@ libglib_la_SOURCES = \ gmain.c \ gmem.c \ gmessages.c \ - gmutex.c \ gnode.c \ gprimes.c \ gqueue.c \ @@ -51,6 +50,7 @@ libglib_la_SOURCES = \ gstack.c \ gstrfuncs.c \ gstring.c \ + gthread.c \ gtimer.c \ gtree.c \ gutils.c diff --git a/acconfig.h b/acconfig.h index aaa035c..1c630e1 100644 --- a/acconfig.h +++ b/acconfig.h @@ -43,7 +43,9 @@ #undef G_THREADS_ENABLED #undef GLIB_SIZEOF_GMUTEX +#undef GLIB_SIZEOF_PTHREAD_T #undef GLIB_BYTE_CONTENTS_GMUTEX +#undef GLIB_BYTE_CONTENTS_GRECMUTEX #undef HAVE_BROKEN_WCTYPE #undef HAVE_DOPRNT @@ -53,6 +55,7 @@ #undef HAVE_LIMITS_H #undef HAVE_LONG_DOUBLE #undef HAVE_POLL +#undef HAVE_PTHREAD_ATTR_SETSTACKSIZE #undef HAVE_PWD_H #undef HAVE_PW_GECOS #undef HAVE_SYS_PARAM_H @@ -94,6 +97,9 @@ #undef NATIVE_WIN32 #undef G_THREAD_SOURCE +#undef POSIX_MIN_PRIORITY +#undef POSIX_MAX_PRIORITY +#undef POSIX_YIELD_FUNC #undef GLIB_NATIVE_BEOS diff --git a/acglib.m4 b/acglib.m4 index 5d3cd93..314d9e4 100644 --- a/acglib.m4 +++ b/acglib.m4 @@ -58,7 +58,7 @@ define(<>, translit(glib_byte_contents_$3, [a-z *], [A-Z_P]))dnl dnl The cache variable name. define(<>, translit(glib_cv_byte_contents_$3, [ *], [_p]))dnl changequote([, ])dnl -AC_MSG_CHECKING(byte contents of $2) +AC_MSG_CHECKING(byte contents of $5) AC_CACHE_VAL(AC_CV_NAME, [AC_TRY_RUN([#include $1 @@ -73,7 +73,7 @@ main() fprintf(f, "%s%d", i?",":"", *(p++)); fprintf(f, "\n"); exit(0); -}], AC_CV_NAME=`cat conftestval`, AC_CV_NAME=0, AC_CV_NAME=0)])dnl +}], AC_CV_NAME=`cat conftestval`, AC_CV_NAME=no, AC_CV_NAME=no)])dnl AC_MSG_RESULT($AC_CV_NAME) AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME) undefine([AC_TYPE_NAME])dnl diff --git a/configure.in b/configure.in index e459f6e..94ba9d5 100644 --- a/configure.in +++ b/configure.in @@ -576,7 +576,7 @@ dnl *********************** dnl *** g_thread checks *** dnl *********************** -AC_ARG_WITH(threads, [ --with-threads=[none/posix/dce/solaris/nspr] specify a thread implementation to use], +AC_ARG_WITH(threads, [ --with-threads=[none/posix/dce/solaris] specify a thread implementation to use], if test "x$with_threads" = x; then want_threads=yes else @@ -646,11 +646,6 @@ if test "x$want_threads" = xyes || test "x$want_threads" = xposix \ fi CPPFLAGS="$glib_save_CPPFLAGS" fi -if test "x$want_threads" = xyes || test "x$want_threads" = xnspr; then - if test "x$have_threads" = xnone; then - AC_CHECK_LIB(nspr21, PRP_NewNakedCondVar, have_threads=nspr) - fi -fi AC_MSG_CHECKING(for thread implementation) @@ -717,11 +712,6 @@ case $have_threads in mutex_header_file='thread.h' g_threads_impl="SOLARIS" ;; - nspr) - AC_CHECK_LIB(nspr21, PRP_NewNakedCondVar, - G_THREAD_LIBS="-lnspr21") - g_threads_impl="NSPR" - ;; none) g_threads_impl="NONE" ;; @@ -830,6 +820,77 @@ if test x"$enable_threads" = xyes; then AC_DEFINE(HAVE_GETPWUID_R_POSIX)]) fi fi + LIBS="$LIBS $G_THREAD_LIBS" + if test x"$have_threads" = xposix; then + GLIB_SIZEOF([#include ], + pthread_t, + pthread_t) + # This is not AC_CHECK_FUNC to also work with function + # name mangling in header files. + AC_MSG_CHECKING(for pthread_attr_setstacksize) + AC_TRY_COMPILE([#include ], + [pthread_attr_setstacksize(NULL,0)], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PTHREAD_ATTR_SETSTACKSIZE)], + [AC_MSG_RESULT(no)]) + # If sched_get_priority_min(SCHED_OTHER) returns something + # negative, we ignore it. This happens on Solaris. + AC_MSG_CHECKING(for minimal/maximal thread priority) + AC_TRY_RUN([#include + int main () + { return sched_get_priority_min(SCHED_OTHER) < 0;}], + [posix_priority_min="sched_get_priority_min(SCHED_OTHER)" + posix_priority_max="sched_get_priority_max(SCHED_OTHER)"], + [posix_priority_min=none]) + if test x"$posix_priority_min" = xnone; then + AC_EGREP_CPP(PX_PRIO_MIN,[#include + PX_PRIO_MIN],,[ + posix_priority_min=PX_PRIO_MIN + posix_priority_max=PX_PRIO_MAX]) + fi + if test x"$posix_priority_min" = xnone; then + AC_EGREP_CPP(PRI_OTHER_MIN,[#include + PRI_OTHER_MIN],,[ + posix_priority_min=PRI_OTHER_MIN + posix_priority_max=PRI_OTHER_MAX]) + fi + if test x"$posix_priority_min" = xnone; then + case $host in + *-*-solaris*) + posix_priority_min=1 + posix_priority_max=127 + ;; + esac + fi + if test x"$posix_priority_min" = xnone; then + AC_MSG_RESULT(none found) + AC_MSG_WARN($POSIX_NO_PRIORITIES) + posix_priority_min=1 + posix_priority_max=1 + else + AC_MSG_RESULT($posix_priority_min/$posix_priority_max) + fi + AC_DEFINE_UNQUOTED(POSIX_MIN_PRIORITY,$posix_priority_min) + AC_DEFINE_UNQUOTED(POSIX_MAX_PRIORITY,$posix_priority_max) + posix_yield_func=none + AC_MSG_CHECKING(for posix yield function) + for yield_func in pthread_yield_np pthread_yield sched_yield \ + thr_yield; do + AC_TRY_LINK([#include ], + [$yield_func()], + [posix_yield_func="$yield_func" + break]) + done + if test x"$posix_yield_func" = xnone; then + AC_MSG_RESULT(none found) + AC_MSG_WARN($POSIX_NO_YIELD) + posix_yield_func="g_thread_sleep(1000)" + else + AC_MSG_RESULT($posix_yield_func) + posix_yield_func="$posix_yield_func()" + fi + AC_DEFINE_UNQUOTED(POSIX_YIELD_FUNC,$posix_yield_func) + fi LIBS="$glib_save_LIBS" CFLAGS="$glib_save_CFLAGS" @@ -865,6 +926,14 @@ GLIB_IF_VAR_EQ(mutex_has_default, yes, gmutex, $glib_cv_sizeof_gmutex, $mutex_default_init) + if test x"$have_threads" = xposix; then + GLIB_BYTE_CONTENTS([#define __USE_GNU +#include <$mutex_header_file>], + $mutex_default_type, + grecmutex, + $glib_cv_sizeof_gmutex, + PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) + fi , ) @@ -1054,6 +1123,32 @@ typedef struct _GMutex* GStaticMutex; #define g_static_mutex_get_mutex(mutex) (g_static_mutex_get_mutex_impl (mutex)) _______EOF fi + if test x$g_recmutex_contents != xno -a \ + x$g_recmutex_contents != x; then + # the definition of GStaticRecMutex is not done via + # typedef GStaticMutex GStaticRecMutex to avoid silent + # compilation, when a GStaticRecMutex is used where a + # GStaticMutex should have been used and vice versa, + # because that micht fail on other platforms. + cat >>$outfile <<_______EOF +typedef struct _GStaticRecMutex GStaticRecMutex; +struct _GStaticRecMutex +{ + struct _GMutex *runtime_mutex; + union { + char pad[$g_mutex_sizeof]; + double dummy_double; + void *dummy_pointer; + long dummy_long; + } aligned_pad_u; +}; +#define G_STATIC_REC_MUTEX_INIT { NULL, { { $g_recmutex_contents} } } +#define g_static_rec_mutex_lock(mutex) g_static_mutex_lock (mutex) +#define g_static_rec_mutex_trylock(mutex) g_static_mutex_trylock (mutex) +#define g_static_rec_mutex_unlock(mutex) g_static_mutex_unlock (mutex) +#define g_static_rec_mutex_get_mutex(mutex) (mutex) +_______EOF + fi echo >>$outfile g_bit_sizes="16 32" @@ -1296,6 +1391,7 @@ g_threads_impl_def=$g_threads_impl g_mutex_has_default="$mutex_has_default" g_mutex_sizeof="$glib_cv_sizeof_gmutex" g_mutex_contents="$glib_cv_byte_contents_gmutex" +g_recmutex_contents="$glib_cv_byte_contents_grecmutex" if test "x$glib_native_beos" = "xyes"; then glib_native_beos_def="\$glib_native_beos_def diff --git a/glib.h b/glib.h index 2587368..0137c82 100644 --- a/glib.h +++ b/glib.h @@ -1500,6 +1500,9 @@ void g_blow_chunks (void); /* Timer */ + +#define G_MICROSEC 1000000 + GTimer* g_timer_new (void); void g_timer_destroy (GTimer *timer); void g_timer_start (GTimer *timer); @@ -1507,7 +1510,7 @@ void g_timer_stop (GTimer *timer); void g_timer_reset (GTimer *timer); gdouble g_timer_elapsed (GTimer *timer, gulong *microseconds); - +void g_usleep (gulong microseconds); /* String utility functions that modify a string argument or * return a constant string that must not be freed. @@ -2367,7 +2370,6 @@ gsize g_date_strftime (gchar *s, const gchar *format, GDate *date); - /* GRelation * * Indexed Relations. Imagine a really simple table in a @@ -2819,31 +2821,63 @@ gint gwin_closedir (DIR *dir); /* GLib Thread support */ + +typedef void (*GThreadFunc) (gpointer value); + +typedef enum +{ + G_THREAD_PRIORITY_LOW, + G_THREAD_PRIORITY_NORMAL, + G_THREAD_PRIORITY_HIGH, + G_THREAD_PRIORITY_URGENT, +} GThreadPriority; + +typedef struct _GThread GThread; +struct _GThread +{ + GThreadPriority priority; + gboolean bound; + gboolean joinable; +}; + typedef struct _GMutex GMutex; typedef struct _GCond GCond; typedef struct _GPrivate GPrivate; typedef struct _GStaticPrivate GStaticPrivate; + typedef struct _GThreadFunctions GThreadFunctions; struct _GThreadFunctions { - GMutex* (*mutex_new) (void); - void (*mutex_lock) (GMutex *mutex); - gboolean (*mutex_trylock) (GMutex *mutex); - void (*mutex_unlock) (GMutex *mutex); - void (*mutex_free) (GMutex *mutex); - GCond* (*cond_new) (void); - void (*cond_signal) (GCond *cond); - void (*cond_broadcast) (GCond *cond); - void (*cond_wait) (GCond *cond, - GMutex *mutex); - gboolean (*cond_timed_wait) (GCond *cond, - GMutex *mutex, - GTimeVal *end_time); - void (*cond_free) (GCond *cond); - GPrivate* (*private_new) (GDestroyNotify destructor); - gpointer (*private_get) (GPrivate *private_key); - void (*private_set) (GPrivate *private_key, - gpointer data); + GMutex* (*mutex_new) (void); + void (*mutex_lock) (GMutex *mutex); + gboolean (*mutex_trylock) (GMutex *mutex); + void (*mutex_unlock) (GMutex *mutex); + void (*mutex_free) (GMutex *mutex); + GCond* (*cond_new) (void); + void (*cond_signal) (GCond *cond); + void (*cond_broadcast) (GCond *cond); + void (*cond_wait) (GCond *cond, + GMutex *mutex); + gboolean (*cond_timed_wait) (GCond *cond, + GMutex *mutex, + GTimeVal *end_time); + void (*cond_free) (GCond *cond); + GPrivate* (*private_new) (GDestroyNotify destructor); + gpointer (*private_get) (GPrivate *private_key); + void (*private_set) (GPrivate *private_key, + gpointer data); + gpointer (*thread_create) (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority); + void (*thread_yield) (void); + void (*thread_join) (gpointer thread); + void (*thread_exit) (void); + void (*thread_set_priority)(gpointer thread, + GThreadPriority priority); + gpointer (*thread_self) (void); }; GUTILS_C_VAR GThreadFunctions g_thread_functions_for_glib_use; @@ -2891,6 +2925,20 @@ GMutex* g_static_mutex_get_mutex_impl (GMutex **mutex); (void) (private_key = \ (GPrivate*) (value)), \ (private_key, value)) +#define g_thread_yield() G_THREAD_CF (thread_yield, (void)0, ()) +#define g_thread_exit() G_THREAD_CF (thread_exit, (void)0, ()) + +GThread* g_thread_create (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority); +GThread* g_thread_self (); +void g_thread_join (GThread* thread); +void g_thread_set_priority (GThread* thread, + GThreadPriority priority); + /* GStaticMutexes can be statically initialized with the value * G_STATIC_MUTEX_INIT, and then they can directly be used, that is * much easier, than having to explicitly allocate the mutex before @@ -2902,6 +2950,7 @@ GMutex* g_static_mutex_get_mutex_impl (GMutex **mutex); g_mutex_trylock (g_static_mutex_get_mutex (mutex)) #define g_static_mutex_unlock(mutex) \ g_mutex_unlock (g_static_mutex_get_mutex (mutex)) + struct _GStaticPrivate { guint index; @@ -2911,6 +2960,51 @@ gpointer g_static_private_get (GStaticPrivate *private_key); void g_static_private_set (GStaticPrivate *private_key, gpointer data, GDestroyNotify notify); +gpointer g_static_private_get_for_thread (GStaticPrivate *private_key, + GThread *thread); +void g_static_private_set_for_thread (GStaticPrivate *private_key, + GThread *thread, + gpointer data, + GDestroyNotify notify); +#ifndef G_STATIC_REC_MUTEX_INIT +/* if GStaticRecMutex is not just a differently initialized GStaticMutex, + * the following is done: + * This can't be done in glibconfig.h, as GStaticPrivate and gboolean + * are not yet known there + */ +typedef struct _GStaticRecMutex GStaticRecMutex; +struct _GStaticRecMutex +{ + GStaticMutex mutex; + GStaticPrivate counter; +}; +#define G_STATIC_REC_MUTEX_INIT { G_STATIC_MUTEX_INIT, G_STATIC_PRIVATE_INIT } +void g_static_rec_mutex_lock (GStaticRecMutex* mutex); +gboolean g_static_rec_mutex_trylock (GStaticRecMutex* mutex); +void g_static_rec_mutex_unlock (GStaticRecMutex* mutex); +#define g_static_rec_mutex_get_mutex(mutex) ((mutex)->mutex) +#endif /* G_STATIC_REC_MUTEX_INIT */ + +typedef struct _GStaticRWLock GStaticRWLock; +struct _GStaticRWLock +{ + GStaticMutex mutex; + GCond *read_cond; + GCond *write_cond; + guint read_counter; + gboolean write; + guint want_to_write; +}; + +#define G_STATIC_RW_LOCK_INIT { G_STATIC_MUTEX_INIT, NULL, NULL, 0, FALSE, FALSE } + +void g_static_rw_lock_reader_lock (GStaticRWLock* lock); +gboolean g_static_rw_lock_reader_trylock (GStaticRWLock* lock); +void g_static_rw_lock_reader_unlock (GStaticRWLock* lock); +void g_static_rw_lock_writer_lock (GStaticRWLock* lock); +gboolean g_static_rw_lock_writer_trylock (GStaticRWLock* lock); +void g_static_rw_lock_writer_unlock (GStaticRWLock* lock); +void g_static_rw_lock_free (GStaticRWLock* lock); /* these are some convenience macros that expand to nothing if GLib * was configured with --disable-threads. for using StaticMutexes, diff --git a/glib/Makefile.am b/glib/Makefile.am index 0725691..f5a8c47 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -40,7 +40,6 @@ libglib_la_SOURCES = \ gmain.c \ gmem.c \ gmessages.c \ - gmutex.c \ gnode.c \ gprimes.c \ gqueue.c \ @@ -51,6 +50,7 @@ libglib_la_SOURCES = \ gstack.c \ gstrfuncs.c \ gstring.c \ + gthread.c \ gtimer.c \ gtree.c \ gutils.c diff --git a/glib/glib.h b/glib/glib.h index 2587368..0137c82 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -1500,6 +1500,9 @@ void g_blow_chunks (void); /* Timer */ + +#define G_MICROSEC 1000000 + GTimer* g_timer_new (void); void g_timer_destroy (GTimer *timer); void g_timer_start (GTimer *timer); @@ -1507,7 +1510,7 @@ void g_timer_stop (GTimer *timer); void g_timer_reset (GTimer *timer); gdouble g_timer_elapsed (GTimer *timer, gulong *microseconds); - +void g_usleep (gulong microseconds); /* String utility functions that modify a string argument or * return a constant string that must not be freed. @@ -2367,7 +2370,6 @@ gsize g_date_strftime (gchar *s, const gchar *format, GDate *date); - /* GRelation * * Indexed Relations. Imagine a really simple table in a @@ -2819,31 +2821,63 @@ gint gwin_closedir (DIR *dir); /* GLib Thread support */ + +typedef void (*GThreadFunc) (gpointer value); + +typedef enum +{ + G_THREAD_PRIORITY_LOW, + G_THREAD_PRIORITY_NORMAL, + G_THREAD_PRIORITY_HIGH, + G_THREAD_PRIORITY_URGENT, +} GThreadPriority; + +typedef struct _GThread GThread; +struct _GThread +{ + GThreadPriority priority; + gboolean bound; + gboolean joinable; +}; + typedef struct _GMutex GMutex; typedef struct _GCond GCond; typedef struct _GPrivate GPrivate; typedef struct _GStaticPrivate GStaticPrivate; + typedef struct _GThreadFunctions GThreadFunctions; struct _GThreadFunctions { - GMutex* (*mutex_new) (void); - void (*mutex_lock) (GMutex *mutex); - gboolean (*mutex_trylock) (GMutex *mutex); - void (*mutex_unlock) (GMutex *mutex); - void (*mutex_free) (GMutex *mutex); - GCond* (*cond_new) (void); - void (*cond_signal) (GCond *cond); - void (*cond_broadcast) (GCond *cond); - void (*cond_wait) (GCond *cond, - GMutex *mutex); - gboolean (*cond_timed_wait) (GCond *cond, - GMutex *mutex, - GTimeVal *end_time); - void (*cond_free) (GCond *cond); - GPrivate* (*private_new) (GDestroyNotify destructor); - gpointer (*private_get) (GPrivate *private_key); - void (*private_set) (GPrivate *private_key, - gpointer data); + GMutex* (*mutex_new) (void); + void (*mutex_lock) (GMutex *mutex); + gboolean (*mutex_trylock) (GMutex *mutex); + void (*mutex_unlock) (GMutex *mutex); + void (*mutex_free) (GMutex *mutex); + GCond* (*cond_new) (void); + void (*cond_signal) (GCond *cond); + void (*cond_broadcast) (GCond *cond); + void (*cond_wait) (GCond *cond, + GMutex *mutex); + gboolean (*cond_timed_wait) (GCond *cond, + GMutex *mutex, + GTimeVal *end_time); + void (*cond_free) (GCond *cond); + GPrivate* (*private_new) (GDestroyNotify destructor); + gpointer (*private_get) (GPrivate *private_key); + void (*private_set) (GPrivate *private_key, + gpointer data); + gpointer (*thread_create) (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority); + void (*thread_yield) (void); + void (*thread_join) (gpointer thread); + void (*thread_exit) (void); + void (*thread_set_priority)(gpointer thread, + GThreadPriority priority); + gpointer (*thread_self) (void); }; GUTILS_C_VAR GThreadFunctions g_thread_functions_for_glib_use; @@ -2891,6 +2925,20 @@ GMutex* g_static_mutex_get_mutex_impl (GMutex **mutex); (void) (private_key = \ (GPrivate*) (value)), \ (private_key, value)) +#define g_thread_yield() G_THREAD_CF (thread_yield, (void)0, ()) +#define g_thread_exit() G_THREAD_CF (thread_exit, (void)0, ()) + +GThread* g_thread_create (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority); +GThread* g_thread_self (); +void g_thread_join (GThread* thread); +void g_thread_set_priority (GThread* thread, + GThreadPriority priority); + /* GStaticMutexes can be statically initialized with the value * G_STATIC_MUTEX_INIT, and then they can directly be used, that is * much easier, than having to explicitly allocate the mutex before @@ -2902,6 +2950,7 @@ GMutex* g_static_mutex_get_mutex_impl (GMutex **mutex); g_mutex_trylock (g_static_mutex_get_mutex (mutex)) #define g_static_mutex_unlock(mutex) \ g_mutex_unlock (g_static_mutex_get_mutex (mutex)) + struct _GStaticPrivate { guint index; @@ -2911,6 +2960,51 @@ gpointer g_static_private_get (GStaticPrivate *private_key); void g_static_private_set (GStaticPrivate *private_key, gpointer data, GDestroyNotify notify); +gpointer g_static_private_get_for_thread (GStaticPrivate *private_key, + GThread *thread); +void g_static_private_set_for_thread (GStaticPrivate *private_key, + GThread *thread, + gpointer data, + GDestroyNotify notify); +#ifndef G_STATIC_REC_MUTEX_INIT +/* if GStaticRecMutex is not just a differently initialized GStaticMutex, + * the following is done: + * This can't be done in glibconfig.h, as GStaticPrivate and gboolean + * are not yet known there + */ +typedef struct _GStaticRecMutex GStaticRecMutex; +struct _GStaticRecMutex +{ + GStaticMutex mutex; + GStaticPrivate counter; +}; +#define G_STATIC_REC_MUTEX_INIT { G_STATIC_MUTEX_INIT, G_STATIC_PRIVATE_INIT } +void g_static_rec_mutex_lock (GStaticRecMutex* mutex); +gboolean g_static_rec_mutex_trylock (GStaticRecMutex* mutex); +void g_static_rec_mutex_unlock (GStaticRecMutex* mutex); +#define g_static_rec_mutex_get_mutex(mutex) ((mutex)->mutex) +#endif /* G_STATIC_REC_MUTEX_INIT */ + +typedef struct _GStaticRWLock GStaticRWLock; +struct _GStaticRWLock +{ + GStaticMutex mutex; + GCond *read_cond; + GCond *write_cond; + guint read_counter; + gboolean write; + guint want_to_write; +}; + +#define G_STATIC_RW_LOCK_INIT { G_STATIC_MUTEX_INIT, NULL, NULL, 0, FALSE, FALSE } + +void g_static_rw_lock_reader_lock (GStaticRWLock* lock); +gboolean g_static_rw_lock_reader_trylock (GStaticRWLock* lock); +void g_static_rw_lock_reader_unlock (GStaticRWLock* lock); +void g_static_rw_lock_writer_lock (GStaticRWLock* lock); +gboolean g_static_rw_lock_writer_trylock (GStaticRWLock* lock); +void g_static_rw_lock_writer_unlock (GStaticRWLock* lock); +void g_static_rw_lock_free (GStaticRWLock* lock); /* these are some convenience macros that expand to nothing if GLib * was configured with --disable-threads. for using StaticMutexes, diff --git a/glib/gthread.c b/glib/gthread.c new file mode 100644 index 0000000..0aaef25 --- /dev/null +++ b/glib/gthread.c @@ -0,0 +1,535 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gmutex.c: MT safety related functions + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * Owen Taylor + * + * 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. + */ + +/* + * Modified by the GLib Team and others 1997-1999. 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 + */ + +#include "glib.h" +#include + +typedef struct _GRealThread GRealThread; + +struct _GRealThread +{ + GThread thread; + GThreadFunc func; + gpointer arg; + gpointer system_thread; + gpointer private_data; +}; + +typedef struct _GStaticPrivateNode GStaticPrivateNode; + +struct _GStaticPrivateNode +{ + gpointer data; + GDestroyNotify destroy; +}; + +static void g_thread_cleanup (gpointer data); +static void g_thread_fail (void); + +/* Global variables */ + +gboolean g_thread_use_default_impl = TRUE; +gboolean g_threads_got_initialized = FALSE; + +GThreadFunctions g_thread_functions_for_glib_use = { + (GMutex*(*)())g_thread_fail, /* mutex_new */ + NULL, /* mutex_lock */ + NULL, /* mutex_trylock */ + NULL, /* mutex_unlock */ + NULL, /* mutex_free */ + (GCond*(*)())g_thread_fail, /* cond_new */ + NULL, /* cond_signal */ + NULL, /* cond_broadcast */ + NULL, /* cond_wait */ + NULL, /* cond_timed_wait */ + NULL, /* cond_free */ + (GPrivate*(*)(GDestroyNotify))g_thread_fail, /* private_new */ + NULL, /* private_get */ + NULL, /* private_set */ + (gpointer(*)(GThreadFunc, gpointer, gulong, + gboolean, gboolean, + GThreadPriority))g_thread_fail, /* thread_create */ + NULL, /* thread_yield */ + NULL, /* thread_join */ + NULL, /* thread_exit */ + NULL, /* thread_set_priority */ + NULL /* thread_self */ +}; + +/* Local data */ + +static GMutex *g_mutex_protect_static_mutex_allocation = NULL; +static GMutex *g_thread_specific_mutex = NULL; +static GPrivate *g_thread_specific_private = NULL; + +/* This must be called only once, before any threads are created. + * It will only be called from g_thread_init() in -lgthread. + */ +void +g_mutex_init (void) +{ + gpointer private_old; + + /* We let the main thread (the one that calls g_thread_init) inherit + * the data, that it set before calling g_thread_init + */ + private_old = g_thread_specific_private; + + g_thread_specific_private = g_private_new (g_thread_cleanup); + + /* we can not use g_private_set here, as g_threads_got_initialized is not + * yet set TRUE, whereas the private_set function is already set. + */ + g_thread_functions_for_glib_use.private_set (g_thread_specific_private, + private_old); + + g_mutex_protect_static_mutex_allocation = g_mutex_new(); + g_thread_specific_mutex = g_mutex_new(); + +} + +GMutex * +g_static_mutex_get_mutex_impl (GMutex** mutex) +{ + if (!g_thread_supported ()) + return NULL; + + g_assert (g_mutex_protect_static_mutex_allocation); + + g_mutex_lock (g_mutex_protect_static_mutex_allocation); + + if (!(*mutex)) + *mutex = g_mutex_new(); + + g_mutex_unlock (g_mutex_protect_static_mutex_allocation); + + return *mutex; +} + +#ifndef g_static_rec_mutex_lock +/* That means, that g_static_rec_mutex_lock is not defined to be + * g_static_mutex_lock, we have to provide an implementation ourselves. + */ +void +g_static_rec_mutex_lock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 0) + { + g_static_mutex_lock (&mutex->mutex); + } + counter++; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); +} + +gboolean +g_static_rec_mutex_trylock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 0) + { + if (!g_static_mutex_trylock (&mutex->mutex)) return FALSE; + } + counter++; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); + return TRUE; +} + +void +g_static_rec_mutex_unlock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 1) + { + g_static_mutex_unlock (&mutex->mutex); + } + counter--; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); +} +#endif /* g_static_rec_mutex_lock */ + +gpointer +g_static_private_get (GStaticPrivate *private_key) +{ + return g_static_private_get_for_thread (private_key, g_thread_self ()); +} + +gpointer +g_static_private_get_for_thread (GStaticPrivate *private_key, + GThread *thread) +{ + GArray *array; + GRealThread *self = (GRealThread*) thread; + + g_return_val_if_fail (thread, NULL); + + array = self->private_data; + if (!array) + return NULL; + + if (!private_key->index) + return NULL; + else if (private_key->index <= array->len) + return g_array_index (array, GStaticPrivateNode, private_key->index - 1).data; + else + return NULL; +} + +void +g_static_private_set (GStaticPrivate *private_key, + gpointer data, + GDestroyNotify notify) +{ + g_static_private_set_for_thread (private_key, g_thread_self (), + data, notify); +} + +void +g_static_private_set_for_thread (GStaticPrivate *private_key, + GThread *thread, + gpointer data, + GDestroyNotify notify) +{ + GArray *array; + GRealThread *self =(GRealThread*) thread; + static guint next_index = 0; + GStaticPrivateNode *node; + + g_return_if_fail (thread); + + array = self->private_data; + if (!array) + { + array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode)); + self->private_data = array; + } + + if (!private_key->index) + { + g_mutex_lock (g_thread_specific_mutex); + + if (!private_key->index) + private_key->index = ++next_index; + + g_mutex_unlock (g_thread_specific_mutex); + } + + if (private_key->index > array->len) + g_array_set_size (array, private_key->index); + + node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1); + if (node->destroy) + { + gpointer ddata = node->data; + GDestroyNotify ddestroy = node->destroy; + + node->data = data; + node->destroy = notify; + + ddestroy (ddata); + } + else + { + node->data = data; + node->destroy = notify; + } +} + +static void +g_thread_cleanup (gpointer data) +{ + if (data) + { + GRealThread* thread = data; + if (thread->private_data) + { + GArray* array = thread->private_data; + guint i; + + for (i = 0; i < array->len; i++ ) + { + GStaticPrivateNode *node = + &g_array_index (array, GStaticPrivateNode, i); + if (node->destroy) + node->destroy (node->data); + } + g_array_free (array, TRUE); + } + /* We only free the thread structure, if it isn't joinable. If + it is, the structure is freed in g_thread_join */ + if (!thread->thread.joinable) + { + /* Just to make sure, this isn't used any more */ + thread->system_thread = NULL; + g_free (thread); + } + } +} + +static void +g_thread_fail (void) +{ + g_error ("The thread system is not yet initialized."); +} + +G_LOCK_DEFINE_STATIC (g_thread_create); + +static void +g_thread_create_proxy (gpointer data) +{ + GRealThread* thread = data; + + g_assert (data); + + /* the lock makes sure, that thread->system_thread is written, + before thread->func is called. See g_thread_create */ + + G_LOCK (g_thread_create); + g_private_set (g_thread_specific_private, data); + G_UNLOCK (g_thread_create); + + thread->func (thread->arg); +} + +GThread* +g_thread_create (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority) +{ + GRealThread* result = g_new0 (GRealThread,1); + + g_return_val_if_fail (thread_func, NULL); + + result->thread.joinable = joinable; + result->thread.bound = bound; + result->thread.priority = priority; + result->func = thread_func; + result->arg = arg; + G_LOCK (g_thread_create); + result->system_thread = G_THREAD_UF (thread_create, (g_thread_create_proxy, + result, stack_size, + joinable, bound, + priority)); + G_UNLOCK (g_thread_create); + return (GThread*) result; +} + +void +g_thread_join (GThread* thread) +{ + GRealThread* real = (GRealThread*) thread; + + g_return_if_fail (thread); + g_return_if_fail (thread->joinable); + g_return_if_fail (real->system_thread); + + G_THREAD_UF (thread_join, (real->system_thread)); + + /* Just to make sure, this isn't used any more */ + thread->joinable = 0; + real->system_thread = NULL; + + /* the thread structure for non-joinable threads is freed upon + thread end. We free the memory here. This will leave loose end, + if a joinable thread is not joined. */ + + g_free (thread); +} + +void +g_thread_set_priority (GThread* thread, + GThreadPriority priority) +{ + GRealThread* real = (GRealThread*) thread; + + g_return_if_fail (thread); + g_return_if_fail (real->system_thread); + + thread->priority = priority; + G_THREAD_CF (thread_set_priority, (void)0, (real->system_thread, priority)); +} + +GThread* +g_thread_self() +{ + GRealThread* thread = g_private_get (g_thread_specific_private); + + if (!thread) + { + /* If no thread data is available, provide and set one. This + can happen for the main thread and for threads, that are not + created by glib. */ + thread = g_new (GRealThread,1); + thread->thread.joinable = FALSE; /* This is a save guess */ + thread->thread.bound = TRUE; /* This isn't important at all */ + thread->thread.priority = G_THREAD_PRIORITY_NORMAL; /* This is + just a guess */ + thread->func = NULL; + thread->arg = NULL; + thread->system_thread = NULL; + thread->private_data = NULL; + g_private_set (g_thread_specific_private, thread); + } + + if (g_thread_supported () && !thread->system_thread) + { + thread->system_thread = g_thread_functions_for_glib_use.thread_self(); + } + + return (GThread*)thread; +} + +static void inline g_static_rw_lock_wait (GCond** cond, GStaticMutex* mutex) +{ + if (!*cond) + *cond = g_cond_new (); + g_cond_wait (*cond, g_static_mutex_get_mutex (mutex)); +} + +static void inline g_static_rw_lock_signal (GStaticRWLock* lock) +{ + if (lock->want_to_write && lock->write_cond) + g_cond_signal (lock->write_cond); + else if (lock->read_cond) + g_cond_signal (lock->read_cond); +} + +void g_static_rw_lock_reader_lock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + while (lock->write || lock->want_to_write) + g_static_rw_lock_wait (&lock->read_cond, &lock->mutex); + lock->read_counter++; + g_static_mutex_unlock (&lock->mutex); +} + +gboolean g_static_rw_lock_reader_trylock (GStaticRWLock* lock) +{ + gboolean ret_val = FALSE; + + g_return_val_if_fail (lock, FALSE); + + if (!g_threads_got_initialized) + return TRUE; + + g_static_mutex_lock (&lock->mutex); + if (!lock->write && !lock->want_to_write) + { + lock->read_counter++; + ret_val = TRUE; + } + g_static_mutex_unlock (&lock->mutex); + return ret_val; +} + +void g_static_rw_lock_reader_unlock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->read_counter--; + g_static_rw_lock_signal (lock); + g_static_mutex_unlock (&lock->mutex); +} + +void g_static_rw_lock_writer_lock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->want_to_write++; + while (lock->write || lock->read_counter) + g_static_rw_lock_wait (&lock->write_cond, &lock->mutex); + lock->want_to_write--; + lock->write = TRUE; + g_static_mutex_unlock (&lock->mutex); +} + +gboolean g_static_rw_lock_writer_trylock (GStaticRWLock* lock) +{ + gboolean ret_val = FALSE; + + g_return_val_if_fail (lock, FALSE); + + if (!g_threads_got_initialized) + return TRUE; + + g_static_mutex_lock (&lock->mutex); + if (!lock->write && !lock->read_counter) + { + lock->write = TRUE; + ret_val = TRUE; + } + g_static_mutex_unlock (&lock->mutex); + return ret_val; +} + +void g_static_rw_lock_writer_unlock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->write = FALSE; + g_static_rw_lock_signal (lock); + g_static_mutex_unlock (&lock->mutex); +} + +void g_static_rw_lock_free (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (lock->read_cond) + g_cond_free (lock->read_cond); + if (lock->write_cond) + g_cond_free (lock->write_cond); + +} + diff --git a/glib/gtimer.c b/glib/gtimer.c index 854ec67..0e61f63 100644 --- a/glib/gtimer.c +++ b/glib/gtimer.c @@ -176,7 +176,7 @@ g_timer_elapsed (GTimer *timer, if (rtimer->start.tv_usec > rtimer->end.tv_usec) { - rtimer->end.tv_usec += 1000000; + rtimer->end.tv_usec += G_MICROSEC; rtimer->end.tv_sec--; } @@ -191,3 +191,13 @@ g_timer_elapsed (GTimer *timer, return total; } + +void +g_usleep (gulong microseconds) +{ + struct timeval tv; + tv.tv_sec = microseconds / G_MICROSEC; + tv.tv_usec = microseconds % G_MICROSEC; + select(0, NULL, NULL, NULL, &tv); +} + diff --git a/glib/gutils.c b/glib/gutils.c index fd468f7..1c80b15 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -478,8 +478,8 @@ g_get_any_init (void) */ if (error == 0 || error == ENOENT) { - g_warning ("getpwuid_r(): failed due to: No such user %d.", - getuid ()); + g_warning ("getpwuid_r(): failed due to: " + "No such user: %lu.", (unsigned long)getuid ()); break; } if (bufsize > 32 * 1024) diff --git a/gmutex.c b/gmutex.c deleted file mode 100644 index d2fe57a..0000000 --- a/gmutex.c +++ /dev/null @@ -1,202 +0,0 @@ -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * gmutex.c: MT safety related functions - * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe - * Owen Taylor - * - * 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. - */ - -/* - * Modified by the GLib Team and others 1997-1999. 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 - */ - -#include "glib.h" - -typedef struct _GStaticPrivateNode GStaticPrivateNode; - -struct _GStaticPrivateNode -{ - gpointer data; - GDestroyNotify destroy; -}; - -static void g_static_private_free_data (gpointer data); -static void g_thread_fail (void); - -/* Global variables */ - -gboolean g_thread_use_default_impl = TRUE; -gboolean g_threads_got_initialized = FALSE; - -GUTILS_C_VAR GThreadFunctions g_thread_functions_for_glib_use = { - (GMutex*(*)())g_thread_fail, /* mutex_new */ - NULL, /* mutex_lock */ - NULL, /* mutex_trylock */ - NULL, /* mutex_unlock */ - NULL, /* mutex_free */ - (GCond*(*)())g_thread_fail, /* cond_new */ - NULL, /* cond_signal */ - NULL, /* cond_broadcast */ - NULL, /* cond_wait */ - NULL, /* cond_timed_wait */ - NULL, /* cond_free */ - (GPrivate*(*)(GDestroyNotify))g_thread_fail, /* private_new */ - NULL, /* private_get */ - NULL, /* private_set */ -}; - -/* Local data */ - -static GMutex *g_mutex_protect_static_mutex_allocation = NULL; -static GMutex *g_thread_specific_mutex = NULL; -static GPrivate *g_thread_specific_private = NULL; - -/* This must be called only once, before any threads are created. - * It will only be called from g_thread_init() in -lgthread. - */ -void -g_mutex_init (void) -{ - /* We let the main thread (the one that calls g_thread_init) inherit - * the data, that it set before calling g_thread_init - */ - gpointer private_old = g_thread_specific_private; - - g_thread_specific_private = g_private_new (g_static_private_free_data); - - /* we can not use g_private_set here, as g_threads_got_initialized is not - * yet set TRUE, whereas the private_set function is already set. - */ - g_thread_functions_for_glib_use.private_set (g_thread_specific_private, - private_old); - - g_mutex_protect_static_mutex_allocation = g_mutex_new(); - g_thread_specific_mutex = g_mutex_new(); -} - -GMutex * -g_static_mutex_get_mutex_impl (GMutex** mutex) -{ - if (!g_thread_supported ()) - return NULL; - - g_assert (g_mutex_protect_static_mutex_allocation); - - g_mutex_lock (g_mutex_protect_static_mutex_allocation); - - if (!(*mutex)) - *mutex = g_mutex_new(); - - g_mutex_unlock (g_mutex_protect_static_mutex_allocation); - - return *mutex; -} - -gpointer -g_static_private_get (GStaticPrivate *private_key) -{ - GArray *array; - - array = g_private_get (g_thread_specific_private); - if (!array) - return NULL; - - if (!private_key->index) - return NULL; - else if (private_key->index <= array->len) - return g_array_index (array, GStaticPrivateNode, private_key->index - 1).data; - else - return NULL; -} - -void -g_static_private_set (GStaticPrivate *private_key, - gpointer data, - GDestroyNotify notify) -{ - GArray *array; - static guint next_index = 0; - GStaticPrivateNode *node; - - array = g_private_get (g_thread_specific_private); - if (!array) - { - array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode)); - g_private_set (g_thread_specific_private, array); - } - - if (!private_key->index) - { - g_mutex_lock (g_thread_specific_mutex); - - if (!private_key->index) - private_key->index = ++next_index; - - g_mutex_unlock (g_thread_specific_mutex); - } - - if (private_key->index > array->len) - g_array_set_size (array, private_key->index); - - node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1); - if (node->destroy) - { - gpointer ddata = node->data; - GDestroyNotify ddestroy = node->destroy; - - node->data = data; - node->destroy = notify; - - ddestroy (ddata); - } - else - { - node->data = data; - node->destroy = notify; - } -} - -static void -g_static_private_free_data (gpointer data) -{ - if (data) - { - GArray* array = data; - guint i; - - for (i = 0; i < array->len; i++ ) - { - GStaticPrivateNode *node = &g_array_index (array, GStaticPrivateNode, i); - if (node->destroy) - node->destroy (node->data); - } - } -} - -static void -g_thread_fail (void) -{ - g_error ("The thread system is not yet initialized."); -} diff --git a/gthread.c b/gthread.c new file mode 100644 index 0000000..0aaef25 --- /dev/null +++ b/gthread.c @@ -0,0 +1,535 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gmutex.c: MT safety related functions + * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe + * Owen Taylor + * + * 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. + */ + +/* + * Modified by the GLib Team and others 1997-1999. 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 + */ + +#include "glib.h" +#include + +typedef struct _GRealThread GRealThread; + +struct _GRealThread +{ + GThread thread; + GThreadFunc func; + gpointer arg; + gpointer system_thread; + gpointer private_data; +}; + +typedef struct _GStaticPrivateNode GStaticPrivateNode; + +struct _GStaticPrivateNode +{ + gpointer data; + GDestroyNotify destroy; +}; + +static void g_thread_cleanup (gpointer data); +static void g_thread_fail (void); + +/* Global variables */ + +gboolean g_thread_use_default_impl = TRUE; +gboolean g_threads_got_initialized = FALSE; + +GThreadFunctions g_thread_functions_for_glib_use = { + (GMutex*(*)())g_thread_fail, /* mutex_new */ + NULL, /* mutex_lock */ + NULL, /* mutex_trylock */ + NULL, /* mutex_unlock */ + NULL, /* mutex_free */ + (GCond*(*)())g_thread_fail, /* cond_new */ + NULL, /* cond_signal */ + NULL, /* cond_broadcast */ + NULL, /* cond_wait */ + NULL, /* cond_timed_wait */ + NULL, /* cond_free */ + (GPrivate*(*)(GDestroyNotify))g_thread_fail, /* private_new */ + NULL, /* private_get */ + NULL, /* private_set */ + (gpointer(*)(GThreadFunc, gpointer, gulong, + gboolean, gboolean, + GThreadPriority))g_thread_fail, /* thread_create */ + NULL, /* thread_yield */ + NULL, /* thread_join */ + NULL, /* thread_exit */ + NULL, /* thread_set_priority */ + NULL /* thread_self */ +}; + +/* Local data */ + +static GMutex *g_mutex_protect_static_mutex_allocation = NULL; +static GMutex *g_thread_specific_mutex = NULL; +static GPrivate *g_thread_specific_private = NULL; + +/* This must be called only once, before any threads are created. + * It will only be called from g_thread_init() in -lgthread. + */ +void +g_mutex_init (void) +{ + gpointer private_old; + + /* We let the main thread (the one that calls g_thread_init) inherit + * the data, that it set before calling g_thread_init + */ + private_old = g_thread_specific_private; + + g_thread_specific_private = g_private_new (g_thread_cleanup); + + /* we can not use g_private_set here, as g_threads_got_initialized is not + * yet set TRUE, whereas the private_set function is already set. + */ + g_thread_functions_for_glib_use.private_set (g_thread_specific_private, + private_old); + + g_mutex_protect_static_mutex_allocation = g_mutex_new(); + g_thread_specific_mutex = g_mutex_new(); + +} + +GMutex * +g_static_mutex_get_mutex_impl (GMutex** mutex) +{ + if (!g_thread_supported ()) + return NULL; + + g_assert (g_mutex_protect_static_mutex_allocation); + + g_mutex_lock (g_mutex_protect_static_mutex_allocation); + + if (!(*mutex)) + *mutex = g_mutex_new(); + + g_mutex_unlock (g_mutex_protect_static_mutex_allocation); + + return *mutex; +} + +#ifndef g_static_rec_mutex_lock +/* That means, that g_static_rec_mutex_lock is not defined to be + * g_static_mutex_lock, we have to provide an implementation ourselves. + */ +void +g_static_rec_mutex_lock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 0) + { + g_static_mutex_lock (&mutex->mutex); + } + counter++; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); +} + +gboolean +g_static_rec_mutex_trylock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 0) + { + if (!g_static_mutex_trylock (&mutex->mutex)) return FALSE; + } + counter++; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); + return TRUE; +} + +void +g_static_rec_mutex_unlock (GStaticRecMutex* mutex) +{ + guint counter = GPOINTER_TO_UINT (g_static_private_get (&mutex->counter)); + if (counter == 1) + { + g_static_mutex_unlock (&mutex->mutex); + } + counter--; + g_static_private_set (&mutex->counter, GUINT_TO_POINTER (counter), NULL); +} +#endif /* g_static_rec_mutex_lock */ + +gpointer +g_static_private_get (GStaticPrivate *private_key) +{ + return g_static_private_get_for_thread (private_key, g_thread_self ()); +} + +gpointer +g_static_private_get_for_thread (GStaticPrivate *private_key, + GThread *thread) +{ + GArray *array; + GRealThread *self = (GRealThread*) thread; + + g_return_val_if_fail (thread, NULL); + + array = self->private_data; + if (!array) + return NULL; + + if (!private_key->index) + return NULL; + else if (private_key->index <= array->len) + return g_array_index (array, GStaticPrivateNode, private_key->index - 1).data; + else + return NULL; +} + +void +g_static_private_set (GStaticPrivate *private_key, + gpointer data, + GDestroyNotify notify) +{ + g_static_private_set_for_thread (private_key, g_thread_self (), + data, notify); +} + +void +g_static_private_set_for_thread (GStaticPrivate *private_key, + GThread *thread, + gpointer data, + GDestroyNotify notify) +{ + GArray *array; + GRealThread *self =(GRealThread*) thread; + static guint next_index = 0; + GStaticPrivateNode *node; + + g_return_if_fail (thread); + + array = self->private_data; + if (!array) + { + array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode)); + self->private_data = array; + } + + if (!private_key->index) + { + g_mutex_lock (g_thread_specific_mutex); + + if (!private_key->index) + private_key->index = ++next_index; + + g_mutex_unlock (g_thread_specific_mutex); + } + + if (private_key->index > array->len) + g_array_set_size (array, private_key->index); + + node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1); + if (node->destroy) + { + gpointer ddata = node->data; + GDestroyNotify ddestroy = node->destroy; + + node->data = data; + node->destroy = notify; + + ddestroy (ddata); + } + else + { + node->data = data; + node->destroy = notify; + } +} + +static void +g_thread_cleanup (gpointer data) +{ + if (data) + { + GRealThread* thread = data; + if (thread->private_data) + { + GArray* array = thread->private_data; + guint i; + + for (i = 0; i < array->len; i++ ) + { + GStaticPrivateNode *node = + &g_array_index (array, GStaticPrivateNode, i); + if (node->destroy) + node->destroy (node->data); + } + g_array_free (array, TRUE); + } + /* We only free the thread structure, if it isn't joinable. If + it is, the structure is freed in g_thread_join */ + if (!thread->thread.joinable) + { + /* Just to make sure, this isn't used any more */ + thread->system_thread = NULL; + g_free (thread); + } + } +} + +static void +g_thread_fail (void) +{ + g_error ("The thread system is not yet initialized."); +} + +G_LOCK_DEFINE_STATIC (g_thread_create); + +static void +g_thread_create_proxy (gpointer data) +{ + GRealThread* thread = data; + + g_assert (data); + + /* the lock makes sure, that thread->system_thread is written, + before thread->func is called. See g_thread_create */ + + G_LOCK (g_thread_create); + g_private_set (g_thread_specific_private, data); + G_UNLOCK (g_thread_create); + + thread->func (thread->arg); +} + +GThread* +g_thread_create (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority) +{ + GRealThread* result = g_new0 (GRealThread,1); + + g_return_val_if_fail (thread_func, NULL); + + result->thread.joinable = joinable; + result->thread.bound = bound; + result->thread.priority = priority; + result->func = thread_func; + result->arg = arg; + G_LOCK (g_thread_create); + result->system_thread = G_THREAD_UF (thread_create, (g_thread_create_proxy, + result, stack_size, + joinable, bound, + priority)); + G_UNLOCK (g_thread_create); + return (GThread*) result; +} + +void +g_thread_join (GThread* thread) +{ + GRealThread* real = (GRealThread*) thread; + + g_return_if_fail (thread); + g_return_if_fail (thread->joinable); + g_return_if_fail (real->system_thread); + + G_THREAD_UF (thread_join, (real->system_thread)); + + /* Just to make sure, this isn't used any more */ + thread->joinable = 0; + real->system_thread = NULL; + + /* the thread structure for non-joinable threads is freed upon + thread end. We free the memory here. This will leave loose end, + if a joinable thread is not joined. */ + + g_free (thread); +} + +void +g_thread_set_priority (GThread* thread, + GThreadPriority priority) +{ + GRealThread* real = (GRealThread*) thread; + + g_return_if_fail (thread); + g_return_if_fail (real->system_thread); + + thread->priority = priority; + G_THREAD_CF (thread_set_priority, (void)0, (real->system_thread, priority)); +} + +GThread* +g_thread_self() +{ + GRealThread* thread = g_private_get (g_thread_specific_private); + + if (!thread) + { + /* If no thread data is available, provide and set one. This + can happen for the main thread and for threads, that are not + created by glib. */ + thread = g_new (GRealThread,1); + thread->thread.joinable = FALSE; /* This is a save guess */ + thread->thread.bound = TRUE; /* This isn't important at all */ + thread->thread.priority = G_THREAD_PRIORITY_NORMAL; /* This is + just a guess */ + thread->func = NULL; + thread->arg = NULL; + thread->system_thread = NULL; + thread->private_data = NULL; + g_private_set (g_thread_specific_private, thread); + } + + if (g_thread_supported () && !thread->system_thread) + { + thread->system_thread = g_thread_functions_for_glib_use.thread_self(); + } + + return (GThread*)thread; +} + +static void inline g_static_rw_lock_wait (GCond** cond, GStaticMutex* mutex) +{ + if (!*cond) + *cond = g_cond_new (); + g_cond_wait (*cond, g_static_mutex_get_mutex (mutex)); +} + +static void inline g_static_rw_lock_signal (GStaticRWLock* lock) +{ + if (lock->want_to_write && lock->write_cond) + g_cond_signal (lock->write_cond); + else if (lock->read_cond) + g_cond_signal (lock->read_cond); +} + +void g_static_rw_lock_reader_lock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + while (lock->write || lock->want_to_write) + g_static_rw_lock_wait (&lock->read_cond, &lock->mutex); + lock->read_counter++; + g_static_mutex_unlock (&lock->mutex); +} + +gboolean g_static_rw_lock_reader_trylock (GStaticRWLock* lock) +{ + gboolean ret_val = FALSE; + + g_return_val_if_fail (lock, FALSE); + + if (!g_threads_got_initialized) + return TRUE; + + g_static_mutex_lock (&lock->mutex); + if (!lock->write && !lock->want_to_write) + { + lock->read_counter++; + ret_val = TRUE; + } + g_static_mutex_unlock (&lock->mutex); + return ret_val; +} + +void g_static_rw_lock_reader_unlock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->read_counter--; + g_static_rw_lock_signal (lock); + g_static_mutex_unlock (&lock->mutex); +} + +void g_static_rw_lock_writer_lock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->want_to_write++; + while (lock->write || lock->read_counter) + g_static_rw_lock_wait (&lock->write_cond, &lock->mutex); + lock->want_to_write--; + lock->write = TRUE; + g_static_mutex_unlock (&lock->mutex); +} + +gboolean g_static_rw_lock_writer_trylock (GStaticRWLock* lock) +{ + gboolean ret_val = FALSE; + + g_return_val_if_fail (lock, FALSE); + + if (!g_threads_got_initialized) + return TRUE; + + g_static_mutex_lock (&lock->mutex); + if (!lock->write && !lock->read_counter) + { + lock->write = TRUE; + ret_val = TRUE; + } + g_static_mutex_unlock (&lock->mutex); + return ret_val; +} + +void g_static_rw_lock_writer_unlock (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (!g_threads_got_initialized) + return; + + g_static_mutex_lock (&lock->mutex); + lock->write = FALSE; + g_static_rw_lock_signal (lock); + g_static_mutex_unlock (&lock->mutex); +} + +void g_static_rw_lock_free (GStaticRWLock* lock) +{ + g_return_if_fail (lock); + + if (lock->read_cond) + g_cond_free (lock->read_cond); + if (lock->write_cond) + g_cond_free (lock->write_cond); + +} + diff --git a/gthread/ChangeLog b/gthread/ChangeLog index f0592f3..51075f5 100644 --- a/gthread/ChangeLog +++ b/gthread/ChangeLog @@ -1,3 +1,19 @@ +1999-06-17 Sebastian Wilhelmi + + * gthread-posix.c, gthread-solaris.c: Added the native + implementations for the GLib's extended thread support. + + * gthread-nspr.c: Removed for good. NSPR is nothing we would want + to build upon. + + * gthread.c: Renamed to gthread-impl.c to avoid confusion with + ../gthread.c (Formerly known as the file called gmutex.c) + + * testgthread.c: Removed. The new and much extended tests are in + ../tests/thread-test.c. + + * Makefile.am: Changed to reflect the changes above. + 1999-03-31 Sebastian Wilhelmi * gthread-posix.c: Use the right default arguments for the diff --git a/gthread/Makefile.am b/gthread/Makefile.am index 48a3bdf..9888fbf 100644 --- a/gthread/Makefile.am +++ b/gthread/Makefile.am @@ -6,7 +6,6 @@ INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gthread \ EXTRA_DIST = \ gthread-posix.c \ gthread-solaris.c \ - gthread-nspr.c \ gthread-none.c \ gthread.def @@ -16,12 +15,9 @@ top_builddir_full=`cd \$(top_builddir); pwd` lib_LTLIBRARIES = libgthread.la -libgthread_la_SOURCES = gthread.c +libgthread_la_SOURCES = gthread-impl.c libgthread_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -release $(LT_RELEASE) libgthread_la_LIBADD = @G_THREAD_LIBS_EXTRA@ @G_THREAD_LIBS@ - -noinst_PROGRAMS = testgthread -testgthread_LDADD = ../libglib.la libgthread.la diff --git a/gthread/gthread.c b/gthread/gthread-impl.c similarity index 76% rename from gthread/gthread.c rename to gthread/gthread-impl.c index 57b75aa..726da39 100644 --- a/gthread/gthread.c +++ b/gthread/gthread-impl.c @@ -38,6 +38,9 @@ #include static gboolean thread_system_already_initialized = FALSE; +static gint g_thread_map_priority (GThreadPriority priority); +static gint g_thread_min_priority = 0; +static gint g_thread_max_priority = 0; #include G_THREAD_SOURCE @@ -99,6 +102,15 @@ g_thread_init (GThreadFunctions* init) g_error ("The supplied thread function vector is invalid."); } + /* now do any initialization stuff required by the implementation, + but only if called with a NULL argument, of course. Otherwise it's + up to the user to do do. */ + +#ifdef HAVE_G_THREAD_IMPL_INIT + if (g_thread_use_default_impl) + g_thread_impl_init(); +#endif + /* now call the thread initialization functions of the different * glib modules. order does matter, g_mutex_init MUST come first. */ @@ -110,4 +122,22 @@ g_thread_init (GThreadFunctions* init) * all the thread functions */ g_threads_got_initialized = TRUE; + + /* we want the main thread to run with normal priority */ + g_thread_set_priority (g_thread_self(), G_THREAD_PRIORITY_NORMAL); +} + +static gint +g_thread_map_priority (GThreadPriority priority) +{ + guint procent; + switch (priority) + { + case G_THREAD_PRIORITY_LOW: procent = 0; break; + default: case G_THREAD_PRIORITY_NORMAL: procent = 40; break; + case G_THREAD_PRIORITY_HIGH: procent = 80; break; + case G_THREAD_PRIORITY_URGENT: procent = 100; break; + } + return g_thread_min_priority + + (g_thread_max_priority - g_thread_min_priority) * procent / 100; } diff --git a/gthread/gthread-nspr.c b/gthread/gthread-nspr.c deleted file mode 100644 index 2fe44b7..0000000 --- a/gthread/gthread-nspr.c +++ /dev/null @@ -1,225 +0,0 @@ -/* 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. - */ - -/* - * Modified by the GLib Team and others 1997-1999. 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 - */ - -#include -#include -#include - -#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_key; - 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_key = malloc (sizeof (GPrivateNSPRData)); - g_assert (private_key); - private_key->data = data; - private_key->destructor = destructor; - - return private_key; -} - -static void -g_private_nspr_data_destructor (gpointer data) -{ - GPrivateNSPRData *private_key = data; - if (private_key->destructor && private_key->data) - (*private_key->destructor) (private_key->data); - free (private_key); -} - -static GPrivate * -g_private_new_nspr_impl (GDestroyNotify destructor) -{ - GPrivateNSPR *result = g_new (GPrivateNSPR, 1); - PRStatus status = PR_NewThreadPrivateIndex (&result->private_key, - 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_key) -{ - GPrivateNSPRData *data; - - STDERR_ASSERT (private_key); - - data = PR_GetThreadPrivate (private_key->private_key); - if (!data) - { - data = g_private_nspr_data_constructor (private_key->destructor, NULL); - STDERR_ASSERT (PR_SetThreadPrivate (private_key->private_key, data) - == PR_SUCCESS); - } - - return data; -} - -static void -g_private_set_nspr_impl (GPrivate * private_key, gpointer value) -{ - if (!private_key) - return; - - g_private_nspr_data_get ((GPrivateNSPR *) private_key)->data = value; -} - -static gpointer -g_private_get_nspr_impl (GPrivate * private_key) -{ - if (!private_key) - return NULL; - - return g_private_nspr_data_get ((GPrivateNSPR *) private_key)->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 index 5611f87..e7c9462 100644 --- a/gthread/gthread-posix.c +++ b/gthread/gthread-posix.c @@ -38,6 +38,19 @@ #include #endif +#if GLIB_SIZEOF_PTHREAD_T == 2 +#define PTHREAD_T_CAST_INT gint16 +#elif GLIB_SIZEOF_PTHREAD_T == 4 +#define PTHREAD_T_CAST_INT gint32 +#elif GLIB_SIZEOF_PTHREAD_T == 8 && defined(G_HAVE_GINT64) +#define PTHREAD_T_CAST_INT gint64 +#else +# error This should not happen. Contact the GLib team. +#endif + +#define GPOINTER_TO_PTHREAD_T(x) ((pthread_t)(PTHREAD_T_CAST_INT)(x)) +#define PTHREAD_T_TO_GPOINTER(x) ((gpointer)(PTHREAD_T_CAST_INT)(x)) + #define posix_print_error( name, num ) \ g_error( "file %s: line %d (%s): error %s during %s", \ __FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, \ @@ -54,12 +67,24 @@ # define posix_check_for_error( what ) G_STMT_START{ \ if( (what) == -1 ) { posix_print_error( what, errno ); } \ }G_STMT_END +# define pthread_key_create(a, b) pthread_keycreate (a, b) +# define pthread_attr_init(a) pthread_attr_create (a) +# define pthread_attr_destroy(a) pthread_attr_delete (a) +# define pthread_create(a, b, c, d) pthread_create(a, &b, c, d) # define mutexattr_default (&pthread_mutexattr_default) # define condattr_default (&pthread_condattr_default) #else /* neither G_THREADS_IMPL_POSIX nor G_THREADS_IMPL_DCE are defined */ -# error This should not happen. Contact the GLb team. +# error This should not happen. Contact the GLib team. #endif +#define HAVE_G_THREAD_IMPL_INIT +static void +g_thread_impl_init() +{ + g_thread_min_priority = POSIX_MIN_PRIORITY; + g_thread_max_priority = POSIX_MAX_PRIORITY; +} + static GMutex * g_mutex_new_posix_impl (void) { @@ -179,7 +204,6 @@ g_private_set_posix_impl (GPrivate * private_key, gpointer value) { if (!private_key) return; - pthread_setspecific (*(pthread_key_t *) private_key, value); } @@ -197,9 +221,93 @@ g_private_get_posix_impl (GPrivate * private_key) private_key, &data)); return data; } -#endif +#endif +} + +gpointer +g_thread_create_posix_impl (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority) +{ + pthread_t thread; + pthread_attr_t attr; + struct sched_param sched; + + g_return_val_if_fail (thread_func, NULL); + + posix_check_for_error (pthread_attr_init (&attr)); + +#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE + if (stack_size) + posix_check_for_error (pthread_attr_setstacksize (&attr, stack_size)); +#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */ + + if (bound) + posix_check_for_error (pthread_attr_setscope (&attr, + PTHREAD_SCOPE_SYSTEM)); + posix_check_for_error( pthread_attr_setdetachstate( &attr, + joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED ) ); + +#ifdef G_THREADS_IMPL_POSIX + posix_check_for_error (pthread_attr_getschedparam (&attr, &sched)); + sched.sched_priority = g_thread_map_priority (priority); + posix_check_for_error (pthread_attr_setschedparam (&attr, &sched)); +#else /* G_THREADS_IMPL_DCE */ + posix_check_for_error + (pthread_attr_setprio (&attr, g_thread_map_priority (priority)); +#endif + + posix_check_for_error( pthread_create (&thread, &attr, + (void* (*)(void*))thread_func, + arg) ); + + posix_check_for_error( pthread_attr_destroy (&attr) ); + + return PTHREAD_T_TO_GPOINTER (thread); +} + +void +g_thread_yield_posix_impl (void) +{ + POSIX_YIELD_FUNC; +} + +void +g_thread_join_posix_impl (gpointer thread) +{ + gpointer ignore; + posix_check_for_error (pthread_join (GPOINTER_TO_PTHREAD_T (thread), + &ignore)); +} + +void +g_thread_exit_posix_impl (void) +{ + pthread_exit (NULL); } +void +g_thread_set_priority_posix_impl (gpointer thread, GThreadPriority priority) +{ + struct sched_param sched; + int policy; + +#ifdef G_THREADS_IMPL_POSIX + posix_check_for_error (pthread_getschedparam (GPOINTER_TO_PTHREAD_T (thread), + &policy, &sched)); + sched.sched_priority = g_thread_map_priority (priority); + posix_check_for_error (pthread_setschedparam (GPOINTER_TO_PTHREAD_T (thread), + policy, &sched)); +#else /* G_THREADS_IMPL_DCE */ + posix_check_for_error (pthread_setprio (GPOINTER_TO_PTHREAD_T (thread), + g_thread_map_priority (priority))); +#endif +} + + static GThreadFunctions g_thread_functions_for_glib_use_default = { g_mutex_new_posix_impl, @@ -215,5 +323,11 @@ static GThreadFunctions g_thread_functions_for_glib_use_default = g_cond_free_posix_impl, g_private_new_posix_impl, g_private_get_posix_impl, - g_private_set_posix_impl + g_private_set_posix_impl, + g_thread_create_posix_impl, + g_thread_yield_posix_impl, + g_thread_join_posix_impl, + g_thread_exit_posix_impl, + g_thread_set_priority_posix_impl, + (gpointer (*)())pthread_self }; diff --git a/gthread/gthread-solaris.c b/gthread/gthread-solaris.c index 4d9c375..1c189b2 100644 --- a/gthread/gthread-solaris.c +++ b/gthread/gthread-solaris.c @@ -46,6 +46,14 @@ if( error ) { solaris_print_error( what, error ); } \ }G_STMT_END +#define HAVE_G_THREAD_IMPL_INIT +static void +g_thread_impl_init() +{ + g_thread_min_priority = 0; + g_thread_max_priority = 127; +} + static GMutex * g_mutex_new_solaris_impl (void) { @@ -165,6 +173,54 @@ g_private_get_solaris_impl (GPrivate * private_key) return result; } +void +g_thread_set_priority_solaris_impl (gpointer thread, GThreadPriority priority) +{ + solaris_check_for_error (thr_setprio (GPOINTER_TO_INT (thread), + g_thread_map_priority (priority))); +} + +gpointer +g_thread_create_solaris_impl (GThreadFunc thread_func, + gpointer arg, + gulong stack_size, + gboolean joinable, + gboolean bound, + GThreadPriority priority) +{ + thread_t thread; + long flags = (bound ? THR_BOUND : 0) | (joinable ? 0: THR_DETACHED); + + g_return_val_if_fail (thread_func, NULL); + + solaris_check_for_error (thr_create (NULL, stack_size, + (void* (*)(void*))thread_func, + arg, flags, &thread)); + + g_thread_set_priority_solaris_impl (GINT_TO_POINTER (thread), priority); + + return GINT_TO_POINTER (thread); +} + +void +g_thread_yield_solaris_impl (void) +{ + thr_yield (); +} + +void +g_thread_join_solaris_impl (gpointer thread) +{ + gpointer ignore; + solaris_check_for_error (thr_join (GPOINTER_TO_INT (thread), NULL, &ignore)); +} + +void +g_thread_exit_solaris_impl (void) +{ + thr_exit (NULL); +} + static GThreadFunctions g_thread_functions_for_glib_use_default = { g_mutex_new_solaris_impl, @@ -180,5 +236,11 @@ static GThreadFunctions g_thread_functions_for_glib_use_default = g_cond_free_solaris_impl, g_private_new_solaris_impl, g_private_get_solaris_impl, - g_private_set_solaris_impl + g_private_set_solaris_impl, + g_thread_create_solaris_impl, + g_thread_yield_solaris_impl, + g_thread_join_solaris_impl, + g_thread_exit_solaris_impl, + g_thread_set_priority_solaris_impl, + (gpointer (*)())thr_self }; diff --git a/gthread/testgthread.c b/gthread/testgthread.c deleted file mode 100644 index 72874cc..0000000 --- a/gthread/testgthread.c +++ /dev/null @@ -1,217 +0,0 @@ -#include "config.h" - -#include - -#define main testglib_main -#include -#undef main - -#define TEST_PRIVATE_THREADS 9 -#define TEST_PRIVATE_ROUNDS 5 - -void -test_mutexes (void) -{ - 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(G_THREADS_IMPL_NSPR) -#warning "note, that you have to link with whatever library" -#warning "nspr is building upon, it might otherwise (as on solaris) lead to" -#warning "run time failure, as the mutex functions are defined in libc, but" -#warning "as noops, that will make some nspr assertions fail." -#include - -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(G_THREADS_IMPL_SOLARIS) -#include - -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(G_THREADS_IMPL_POSIX) -#include - -gpointer -new_thread(GHookFunc func, gpointer data) -{ - pthread_t thread; - pthread_attr_t pthread_attr; - pthread_attr_init (&pthread_attr); - /* This is the default, it seems, so leave that out for now - 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 (void) -{ - 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_key; - -void -test_private_func (void *data) -{ - guint i = 0; - static unsigned int seed = 0; - if (!seed) - { - GTimeVal now; - g_get_current_time (&now); - seed = now.tv_usec; - } - wait_thread (1); - while (i < TEST_PRIVATE_ROUNDS) - { -#ifdef HAVE_RAND_R - guint random_value = rand_r (&seed) % 10000; -#else - guint random_value = rand() % 10000; -#endif - guint *data = g_static_private_get (&private_key); - if (!data) - { - data = private_constructor (); - g_static_private_set (&private_key, data, private_destructor); - } - *data = random_value; - wait_thread (.2); - g_assert (*(guint *) g_static_private_get (&private_key) == random_value); - i++; - } -} - -void -test_private (void) -{ - int i; - gpointer threads[TEST_PRIVATE_THREADS]; - for (i = 0; i < TEST_PRIVATE_THREADS; i++) - { - threads[i] = new_thread (test_private_func, GINT_TO_POINTER(i)); - } - for (i = 0; i < TEST_PRIVATE_THREADS; i++) - { - join_thread (threads[i]); - } - g_print ("\n"); -} - -int -main (void) -{ - 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; -} diff --git a/gtimer.c b/gtimer.c index 854ec67..0e61f63 100644 --- a/gtimer.c +++ b/gtimer.c @@ -176,7 +176,7 @@ g_timer_elapsed (GTimer *timer, if (rtimer->start.tv_usec > rtimer->end.tv_usec) { - rtimer->end.tv_usec += 1000000; + rtimer->end.tv_usec += G_MICROSEC; rtimer->end.tv_sec--; } @@ -191,3 +191,13 @@ g_timer_elapsed (GTimer *timer, return total; } + +void +g_usleep (gulong microseconds) +{ + struct timeval tv; + tv.tv_sec = microseconds / G_MICROSEC; + tv.tv_usec = microseconds % G_MICROSEC; + select(0, NULL, NULL, NULL, &tv); +} + diff --git a/gutils.c b/gutils.c index fd468f7..1c80b15 100644 --- a/gutils.c +++ b/gutils.c @@ -478,8 +478,8 @@ g_get_any_init (void) */ if (error == 0 || error == ENOENT) { - g_warning ("getpwuid_r(): failed due to: No such user %d.", - getuid ()); + g_warning ("getpwuid_r(): failed due to: " + "No such user: %lu.", (unsigned long)getuid ()); break; } if (bufsize > 32 * 1024) diff --git a/tests/Makefile.am b/tests/Makefile.am index b735dd9..2abdb0c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -20,7 +20,8 @@ TESTS = \ string-test \ strfunc-test \ tree-test \ - type-test + type-test \ + thread-test noinst_PROGRAMS = $(TESTS) @@ -39,6 +40,8 @@ string_test_LDADD = $(top_builddir)/libglib.la strfunc_test_LDADD = $(top_builddir)/libglib.la tree_test_LDADD = $(top_builddir)/libglib.la type_test_LDADD = $(top_builddir)/libglib.la +thread_test_LDADD = $(top_builddir)/libglib.la \ + $(top_builddir)/gthread/libgthread.la @G_THREAD_LIBS@ makefile.msc: $(top_builddir)/config.status $(top_srcdir)/tests/makefile.msc.in cd $(top_builddir) && CONFIG_FILES=tests/$@ CONFIG_HEADERS= $(SHELL) ./config.status diff --git a/tests/thread-test.c b/tests/thread-test.c new file mode 100644 index 0000000..300e55e --- /dev/null +++ b/tests/thread-test.c @@ -0,0 +1,238 @@ +#include + +/* GMutex */ + +static GMutex* test_g_mutex_mutex = NULL; +static guint test_g_mutex_int = 0; + +static void +test_g_mutex_thread (gpointer data) +{ + g_assert (GPOINTER_TO_INT (data) == 42); + g_assert (g_mutex_trylock (test_g_mutex_mutex) == FALSE); + g_mutex_lock (test_g_mutex_mutex); + g_assert (test_g_mutex_int == 42); + g_mutex_unlock (test_g_mutex_mutex); +} + +static void +test_g_mutex (void) +{ + GThread *thread; + test_g_mutex_mutex = g_mutex_new (); + + g_assert (g_mutex_trylock (test_g_mutex_mutex)); + thread = g_thread_create (test_g_mutex_thread, + GINT_TO_POINTER (42), + 0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL); + g_usleep (G_MICROSEC); + test_g_mutex_int = 42; + g_mutex_unlock (test_g_mutex_mutex); + g_thread_join (thread); + g_mutex_free (test_g_mutex_mutex); +} + +/* GStaticRecMutex */ + +static GStaticRecMutex test_g_static_rec_mutex_mutex = G_STATIC_REC_MUTEX_INIT; +static guint test_g_static_rec_mutex_int = 0; + +static void +test_g_static_rec_mutex_thread (gpointer data) +{ + g_assert (GPOINTER_TO_INT (data) == 42); + g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex) + == FALSE); + g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex); + g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex); + g_assert (test_g_static_rec_mutex_int == 42); + g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex); + g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex); +} + +static void +test_g_static_rec_mutex (void) +{ + GThread *thread; + + g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex)); + thread = g_thread_create (test_g_static_rec_mutex_thread, + GINT_TO_POINTER (42), + 0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL); + g_usleep (G_MICROSEC); + g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex)); + g_usleep (G_MICROSEC); + test_g_static_rec_mutex_int = 41; + g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex); + test_g_static_rec_mutex_int = 42; + g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex); + g_usleep (G_MICROSEC); + g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex); + test_g_static_rec_mutex_int = 0; + g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex); + g_thread_join (thread); +} + +/* GStaticPrivate */ + +#define THREADS 10 + +static GStaticPrivate test_g_static_private_private = G_STATIC_PRIVATE_INIT; +static GStaticMutex test_g_static_private_mutex = G_STATIC_MUTEX_INIT; +static guint test_g_static_private_counter = 0; + +static gpointer +test_g_static_private_constructor (void) +{ + g_static_mutex_lock (&test_g_static_private_mutex); + test_g_static_private_counter++; + g_static_mutex_unlock (&test_g_static_private_mutex); + return g_new (guint,1); +} + +static void +test_g_static_private_destructor (gpointer data) +{ + g_static_mutex_lock (&test_g_static_private_mutex); + test_g_static_private_counter--; + g_static_mutex_unlock (&test_g_static_private_mutex); + g_free (data); +} + + +static void +test_g_static_private_thread (gpointer data) +{ + guint number = GPOINTER_TO_INT (data); + guint i; + guint* private; + for (i = 0; i < 10; i++) + { + number = number * 11 + 1; /* A very simple and bad RNG ;-) */ + private = g_static_private_get (&test_g_static_private_private); + if (!private || number % 7 > 3) + { + private = test_g_static_private_constructor (); + g_static_private_set (&test_g_static_private_private, private, + test_g_static_private_destructor); + } + *private = number; + g_usleep (G_MICROSEC / 5); + g_assert (number == *private); + } +} + +static void +test_g_static_private (void) +{ + GThread *threads[THREADS]; + guint i; + for (i = 0; i < THREADS; i++) + { + threads[i] = g_thread_create (test_g_static_private_thread, + GINT_TO_POINTER (i), + 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL); + } + for (i = 0; i < THREADS; i++) + { + g_thread_join (threads[i]); + } + g_assert (test_g_static_private_counter == 0); +} + +/* GStaticRWLock */ + +/* -1 = writing; >0 = # of readers */ +static gint test_g_static_rw_lock_state = 0; +G_LOCK_DEFINE (test_g_static_rw_lock_state); + +static gboolean test_g_static_rw_lock_run = TRUE; +static GStaticRWLock test_g_static_rw_lock_lock = G_STATIC_RW_LOCK_INIT; + +static void +test_g_static_rw_lock_thread (gpointer data) +{ + while (test_g_static_rw_lock_run) + { + if (g_random_double() > .2) /* I'm a reader */ + { + + if (g_random_double() > .2) /* I'll block */ + g_static_rw_lock_reader_lock (&test_g_static_rw_lock_lock); + else /* I'll only try */ + if (!g_static_rw_lock_reader_trylock (&test_g_static_rw_lock_lock)) + continue; + G_LOCK (test_g_static_rw_lock_state); + g_assert (test_g_static_rw_lock_state >= 0); + test_g_static_rw_lock_state++; + G_UNLOCK (test_g_static_rw_lock_state); + + g_usleep (10); + + G_LOCK (test_g_static_rw_lock_state); + test_g_static_rw_lock_state--; + G_UNLOCK (test_g_static_rw_lock_state); + + g_static_rw_lock_reader_unlock (&test_g_static_rw_lock_lock); + } + else /* I'm a writer */ + { + + if (g_random_double() > .2) /* I'll block */ + g_static_rw_lock_writer_lock (&test_g_static_rw_lock_lock); + else /* I'll only try */ + if (!g_static_rw_lock_writer_trylock (&test_g_static_rw_lock_lock)) + continue; + G_LOCK (test_g_static_rw_lock_state); + g_assert (test_g_static_rw_lock_state == 0); + test_g_static_rw_lock_state = -1; + G_UNLOCK (test_g_static_rw_lock_state); + + g_usleep (10); + + G_LOCK (test_g_static_rw_lock_state); + test_g_static_rw_lock_state = 0; + G_UNLOCK (test_g_static_rw_lock_state); + + g_static_rw_lock_writer_unlock (&test_g_static_rw_lock_lock); + } + } +} + +static void +test_g_static_rw_lock () +{ + GThread *threads[THREADS]; + guint i; + for (i = 0; i < THREADS; i++) + { + threads[i] = g_thread_create (test_g_static_rw_lock_thread, + 0, 0, TRUE, TRUE, + G_THREAD_PRIORITY_NORMAL); + } + g_usleep (G_MICROSEC); + test_g_static_rw_lock_run = FALSE; + for (i = 0; i < THREADS; i++) + { + g_thread_join (threads[i]); + } + g_assert (test_g_static_rw_lock_state == 0); +} + +/* run all the tests */ +int +main (int argc, + char *argv[]) +{ + /* Only run the test, if threads are enabled and a default thread + implementation is available */ +#if defined(G_THREADS_ENABLED) && ! defined(G_THREADS_IMPL_NONE) + g_thread_init (NULL); + test_g_mutex (); + test_g_static_rec_mutex (); + test_g_static_private (); + test_g_static_rw_lock (); +#endif + return 0; +} -- 2.7.4