diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h new file mode 100644 index 0000000..3c7b933 --- /dev/null +++ b/lib/glthread/cond.h @@ -0,0 +1,325 @@ +/* Condition waiting in multithreaded situations. + Copyright (C) 2005-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Yoann Vandoorselaere , 2008. + Based on Bruno Haible lock.h */ + +/* + Condition variables can be used for waiting until a condition + becomes true. In this respect, they are similar to wait queues. But + contrary to wait queues, condition variables have different + semantics that allows events to be lost when there is no thread + waiting for them. + + Condition variable: + Type: gl_cond_t + Declaration: gl_cond_define(extern, name) + Initializer: gl_cond_define_initialized(, name) + Waiting: gl_cond_wait(name) + Timed wait: gl_cond_timedwait(name, tv) + Signaling: gl_cond_signal(name) + Broadcasting: gl_cond_broadcast(name) +*/ + + +#ifndef _GLCOND_H +#define _GLCOND_H + +#include +#include "glthread/lock.h" + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if PTHREAD_IN_USE_DETECTION_HARD + +/* The pthread_in_use() detection needs to be done at runtime. */ +# define pthread_in_use() \ + glthread_in_use () +extern int glthread_in_use (void); + +# endif + +# if USE_POSIX_THREADS_WEAK + +/* Use weak references to the POSIX threads library. */ + +/* Weak references avoid dragging in external libraries if the other parts + of the program don't use them. Here we use them, because we don't want + every program that uses libintl to depend on libpthread. This assumes + that libpthread would not be loaded after libintl; i.e. if libintl is + loaded first, by an executable that does not depend on libpthread, and + then a module is dynamically loaded that depends on libpthread, libintl + will not be multithread-safe. */ + +/* The way to test at runtime whether libpthread is present is to test + whether a function pointer's value, such as &pthread_mutex_init, is + non-NULL. However, some versions of GCC have a bug through which, in + PIC mode, &foo != NULL always evaluates to true if there is a direct + call to foo(...) in the same function. To avoid this, we test the + address of a function in libpthread that we don't use. */ + +# pragma weak pthread_cond_init +# pragma weak pthread_cond_wait +# pragma weak pthread_cond_timedwait +# pragma weak pthread_cond_signal +# pragma weak pthread_cond_broadcast +# pragma weak pthread_cond_destroy + +# if !PTHREAD_IN_USE_DETECTION_HARD +# pragma weak pthread_cancel +# define pthread_in_use() (pthread_cancel != NULL) +# endif + +# else + +# if !PTHREAD_IN_USE_DETECTION_HARD +# define pthread_in_use() 1 +# endif + +# endif + + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +typedef pthread_cond_t gl_cond_t; +# define gl_cond_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_cond_t NAME; +# define gl_cond_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pthread_cond_t NAME = gl_cond_initializer; +# define gl_cond_initializer \ + PTHREAD_COND_INITIALIZER +# define glthread_cond_init(COND) \ + (pthread_in_use () ? pthread_cond_init (COND, NULL) : 0) +# define glthread_cond_wait(COND, LOCK) \ + (pthread_in_use () ? pthread_cond_wait (COND, LOCK) : 0) +# define glthread_cond_timedwait(COND, LOCK, TS) \ + (pthread_in_use () ? pthread_cond_timedwait (COND, LOCK, TS) : 0) +# define glthread_cond_signal(COND) \ + (pthread_in_use () ? pthread_cond_signal (COND) : 0) +# define glthread_cond_broadcast(COND) \ + (pthread_in_use () ? pthread_cond_broadcast (COND) : 0) +# define glthread_cond_destroy(COND) \ + (pthread_in_use () ? pthread_cond_destroy (COND) : 0) + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_PTH_THREADS_WEAK + +/* Use weak references to the GNU Pth threads library. */ + +# pragma weak pth_cond_init +# pragma weak pth_cond_await +# pragma weak pth_cond_notify +# pragma weak pth_event +# pragma weak pth_timeout +# pragma weak pth_cancel +# define pth_in_use() (pth_cancel != NULL) + +# else + +# define pth_in_use() 1 + +# endif + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +typedef pth_cond_t gl_cond_t; +# define gl_cond_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_cond_t NAME; +# define gl_cond_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS pth_cond_t NAME = gl_cond_initializer; +# define gl_cond_initializer \ + PTH_COND_INIT +# define glthread_cond_init(COND) \ + (pth_in_use () && !pth_cond_init (COND) ? errno : 0) +# define glthread_cond_wait(COND, LOCK) \ + (pth_in_use () && !pth_cond_await (COND, LOCK, NULL) ? errno : 0) + +static inline int +glthread_cond_timedwait (gl_cond_t * cond, + gl_lock_t * lock, + struct timespec *ts) +{ + int ret, status; + pth_event_t ev; + + if (!pth_in_use ()) + return 0; + + ev = pth_event (PTH_EVENT_TIME, pth_time (ts->tv_sec, ts->tv_nsec / 1000)); + ret = pth_cond_await (cond, lock, ev); + + status = pth_event_status (ev); + pth_event_free (ev, PTH_FREE_THIS); + + if (status == PTH_STATUS_OCCURRED) + return ETIMEDOUT; + + return ret; +} + +# define glthread_cond_signal(COND) \ + (pth_in_use () && !pth_cond_notify (COND, FALSE) ? errno : 0) +# define glthread_cond_broadcast(COND) \ + (pth_in_use () && !pth_cond_notify (COND, TRUE) ? errno : 0) +# define glthread_cond_destroy(COND) 0 + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +# include +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_SOLARIS_THREADS_WEAK + +/* Use weak references to the old Solaris threads library. */ + +# pragma weak cond_init +# pragma weak cond_wait +# pragma weak cond_timedwait +# pragma weak cond_signal +# pragma weak cond_broadcast +# pragma weak cond_destroy +# pragma weak thr_suspend +# define thread_in_use() (thr_suspend != NULL) + +# else + +# define thread_in_use() 1 + +# endif + +/* -------------------------- gl_cond_t datatype -------------------------- */ + +#define ETIMEDOUT ETIME + +typedef pthread_cond_t gl_cond_t; +# define gl_cond_define(STORAGECLASS, NAME) \ + STORAGECLASS cond_t NAME; +# define gl_cond_define_initialized(STORAGECLASS, NAME) \ + STORAGECLASS cond_t NAME = gl_cond_initializer; +# define gl_cond_initializer \ + DEFAULTCV +# define glthread_cond_init(COND) \ + (pthread_in_use () ? cond_init (COND, USYNC_THREAD, NULL) : 0) +# define glthread_cond_wait(COND, LOCK) \ + (pthread_in_use () ? cond_wait (COND, LOCK) : 0) +# define glthread_cond_timedwait(COND, LOCK, TS) \ + (pthread_in_use () ? cond_timedwait (COND, LOCK, TS) : 0) +# define glthread_cond_signal(COND) \ + (pthread_in_use () ? cond_signal (COND) : 0) +# define glthread_cond_broadcast(COND) \ + (pthread_in_use () ? cond_broadcast (COND) : 0) +# define glthread_cond_destroy(COND) \ + (pthread_in_use () ? cond_destroy (COND) : 0) + +#endif + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) + +/* Provide dummy implementation if threads are not supported. */ + +typedef int gl_cond_t; +# define gl_cond_define(STORAGECLASS, NAME) +# define gl_cond_define_initialized(STORAGECLASS, NAME) +# define glthread_cond_init(COND) 0 +# define glthread_cond_signal(COND) 0 +# define glthread_cond_broadcast(COND) 0 +# define glthread_cond_wait(COND, LOCK) 0 +# define glthread_cond_timedwait(COND, LOCK, TS) 0 +# define glthread_cond_destroy(COND) 0 + +#endif + + +/* ========================================================================= */ + +/* Macros with built-in error handling. */ + +#define gl_cond_init(COND) \ + do \ + { \ + if (glthread_cond_init (&COND)) \ + abort (); \ + } \ + while (0) +#define gl_cond_wait(COND, LOCK) \ + do \ + { \ + if (glthread_cond_wait (&COND, &LOCK)) \ + abort (); \ + } \ + while (0) +#define gl_cond_signal(COND) \ + do \ + { \ + if (glthread_cond_signal (&COND)) \ + abort (); \ + } \ + while (0) +#define gl_cond_broadcast(COND) \ + do \ + { \ + if (glthread_cond_broadcast (&COND)) \ + abort (); \ + } \ + while (0) +#define gl_cond_destroy(COND) \ + do \ + { \ + if (glthread_cond_destroy (&COND)) \ + abort (); \ + } \ + while (0) + +#endif /* _GLCOND_H */ diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h new file mode 100644 index 0000000..1091d29 --- /dev/null +++ b/lib/glthread/thread.h @@ -0,0 +1,276 @@ +/* Locking in multithreaded situations. + Copyright (C) 2005-2008 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Bruno Haible , 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +/* This file contains locking primitives for use with a given thread library. + It does not contain primitives for creating threads or for other + synchronization primitives. +*/ + + +#ifndef _GLTHREAD_H +#define _GLTHREAD_H + +#include + +/* ========================================================================= */ + +#if USE_POSIX_THREADS + +/* Use the POSIX threads library. */ + +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if PTHREAD_IN_USE_DETECTION_HARD + +/* The pthread_in_use() detection needs to be done at runtime. */ +# define pthread_in_use() \ + glthread_in_use () +extern int glthread_in_use (void); + +# endif + +# if USE_POSIX_THREADS_WEAK + +/* Use weak references to the POSIX threads library. */ + +/* Weak references avoid dragging in external libraries if the other parts + of the program don't use them. Here we use them, because we don't want + every program that uses libintl to depend on libpthread. This assumes + that libpthread would not be loaded after libintl; i.e. if libintl is + loaded first, by an executable that does not depend on libpthread, and + then a module is dynamically loaded that depends on libpthread, libintl + will not be multithread-safe. */ + +/* The way to test at runtime whether libpthread is present is to test + whether a function pointer's value, such as &pthread_mutex_init, is + non-NULL. However, some versions of GCC have a bug through which, in + PIC mode, &foo != NULL always evaluates to true if there is a direct + call to foo(...) in the same function. To avoid this, we test the + address of a function in libpthread that we don't use. */ + +# pragma weak pthread_create +# pragma weak pthread_join +# pragma weak pthread_self +# pragma weak pthread_exit +# pragma weak pthread_sigmask + +# ifdef HAVE_PTHREAD_ATFORK +# pragma weak pthread_atfork +# endif + +# if !PTHREAD_IN_USE_DETECTION_HARD +# pragma weak pthread_cancel +# define pthread_in_use() (pthread_cancel != NULL) +# endif + +# else + +# if !PTHREAD_IN_USE_DETECTION_HARD +# define pthread_in_use() 1 +# endif + +# endif + + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +typedef pthread_t gl_thread_t; +# define glthread_create(THREAD, FUNC, ARG) \ + (pthread_in_use () ? pthread_create (THREAD, NULL, FUNC, ARG) : 0) +# define glthread_sigmask(HOW, SET, OSET) \ + (pthread_in_use () ? pthread_sigmask (HOW, SET, OSET) : 0) +# define glthread_join(THREAD, VALPTR) \ + (pthread_in_use () ? pthread_join (THREAD, VALPTR) : 0) +# define glthread_self() \ + (pthread_in_use () ? (void *) pthread_self () : 0) +# define glthread_exit(VALPTR) \ + (pthread_in_use () ? pthread_exit (VALPTR) : 0) + +# ifdef HAVE_PTHREAD_ATFORK +# define glthread_atfork(PREPARE, PARENT, CHILD) \ + (pthread_in_use () ? pthread_atfork (PREPARE, PARENT, CHILD) : 0) +# else +# define glthread_atfork(PREPARE, PARENT, CHILD) 0 +# endif + +#endif + +/* ========================================================================= */ + +#if USE_PTH_THREADS + +/* Use the GNU Pth threads library. */ + +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_PTH_THREADS_WEAK + +/* Use weak references to the GNU Pth threads library. */ + +# pragma weak pth_spawn +# pragma weak pth_sigmask +# pragma weak pth_join +# pragma weak pth_self +# pragma weak pth_exit +# pragma weak pth_cancel +# define pth_in_use() (pth_cancel != NULL) + +# else + +# define pth_in_use() 1 + +# endif +/* -------------------------- gl_thread_t datatype -------------------------- */ + +typedef pth_t gl_thread_t; +# define glthread_create(THREAD, FUNC, ARG) \ + (pth_in_use () ? ((*(THREAD) = pth_spawn (NULL, FUNC, ARG)) ? 0 : -1) : 0) +# define glthread_sigmask(HOW, SET, OSET) \ + (pth_in_use && !pth_sigmask (HOW, SET, OSET) ? errno : 0) +# define glthread_join(THREAD, VALPTR) \ + (pth_in_use () && !pth_join (THREAD, VALPTR) ? errno : 0) +# define glthread_self() \ + (pth_in_use () ? (void *) pth_self () : 0) +# define glthread_exit(VALPTR) \ + (pth_in_use () ? pth_exit (VALPTR) : 0) +# define glthread_atfork(PREPARE, PARENT, CHILD) 0 + +#endif + +/* ========================================================================= */ + +#if USE_SOLARIS_THREADS + +/* Use the old Solaris threads library. */ + +# include +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +# if USE_SOLARIS_THREADS_WEAK + +/* Use weak references to the old Solaris threads library. */ + +# pragma weak thr_create +# pragma weak thr_join +# pragma weak thr_self +# pragma weak thr_exit +# pragma weak thr_suspend +# define thread_in_use() (thr_suspend != NULL) + +# else + +# define thread_in_use() 1 + +# endif + +/* -------------------------- gl_thread_t datatype -------------------------- */ + +typedef thread_t gl_thread_t; +# define glthread_create(THREAD, FUNC, ARG) \ + (thread_in_use () ? thr_create (NULL, 0, FUNC, ARG, 0, THREAD) : 0) +# define glthread_sigmask(HOW, SET, OSET) \ + (pthread_in_use () ? sigprocmask (HOW, SET, OSET) : 0) +# define glthread_join(THREAD, RETVAL) \ + (pthread_in_use () ? thr_join (THREAD, NULL, RETVAL) : 0) +# define glthread_self() \ + (pthread_in_use () ? (void *) thr_self () : 0) +# define glthread_exit(VALPTR) \ + (pthread_in_use () ? thr_exit (VALPTR) : 0) +# define glthread_atfork(PREPARE, PARENT, CHILD) 0 +#endif + + +/* ========================================================================= */ + +#if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS) + +/* Provide dummy implementation if threads are not supported. */ + +typedef int gl_thread_t; +# define glthread_create(THREAD, FUNC, ARG) 0 +# define glthread_sigmask(HOW, SET, OSET) 0 +# define glthread_join(THREAD, RETVAL) 0 +# define glthread_self() NULL +# define glthread_exit(VALPTR) 0 +# define glthread_atfork(PREPARE, PARENT, CHILD) 0 + +#endif + + +/* ========================================================================= */ + +/* Macros with built-in error handling. */ + +static inline int +gl_thread_create_func (gl_thread_t * thread, + void *(*func) (void *arg), + void *arg) +{ + int ret; + + ret = glthread_create (thread, func, arg); + if (ret) + abort (); + + return ret; +} + +#define gl_thread_create(THREAD, FUNC, ARG) \ + gl_thread_create_func(&THREAD, FUNC, ARG) + +#define gl_thread_sigmask(HOW, SET, OSET) \ + do \ + { \ + if (glthread_sigmask (HOW, SET, OSET)) \ + abort (); \ + } \ + while (0) +#define gl_thread_join(THREAD, RETVAL) \ + do \ + { \ + if (glthread_join (THREAD, RETVAL)) \ + abort (); \ + } \ + while (0) +#define gl_thread_atfork(PREPARE, PARENT, CHILD) \ + do \ + { \ + if (glthread_atfork (PREPARE, PARENT, CHILD)) \ + abort (); \ + } \ + while (0) + +#endif /* _GLCOND_H */ diff --git a/m4/cond.m4 b/m4/cond.m4 new file mode 100644 index 0000000..b98d51e --- /dev/null +++ b/m4/cond.m4 @@ -0,0 +1,11 @@ +# glcond.m4 serial 1 (gettext-0.15) +dnl Copyright (C) 2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_COND], +[ + AC_C_INLINE() + AC_REQUIRE([gl_LOCK]) +]) diff --git a/m4/thread.m4 b/m4/thread.m4 new file mode 100644 index 0000000..43bd94d --- /dev/null +++ b/m4/thread.m4 @@ -0,0 +1,15 @@ +# glcond.m4 serial 1 (gettext-0.15) +dnl Copyright (C) 2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_THREAD], +[ + AC_C_INLINE() + AC_REQUIRE([gl_LOCK]) + + if test $gl_threads_api = posix; then + AC_CHECK_FUNC(pthread_atfork) + fi +]) diff --git a/modules/cond b/modules/cond new file mode 100644 index 0000000..5cf6bad --- /dev/null +++ b/modules/cond @@ -0,0 +1,25 @@ +Description: +Condition in multithreaded situations. + +Files: +lib/glthread/cond.h +m4/cond.m4 + +Depends-on: +lock + +configure.ac: +gl_COND + +Makefile.am: +lib_SOURCES += glthread/cond.h + +Include: +"glthread/cond.h" + +License: +LGPLv2+ + +Maintainer: +Yoann Vandoorselaere + diff --git a/modules/cond-tests b/modules/cond-tests new file mode 100644 index 0000000..72edd2a --- /dev/null +++ b/modules/cond-tests @@ -0,0 +1,22 @@ +Files: +tests/test-cond.c + +Depends-on: +lock + +configure.ac: +dnl Checks for special libraries for the tests/test-lock test. +dnl On some systems, sched_yield is in librt, rather than in libpthread. +LIBSCHED= +if test $gl_threads_api = posix; then + dnl Solaris has sched_yield in librt, not in libpthread or libc. + AC_CHECK_LIB(rt, sched_yield, [LIBSCHED=-lrt], + [dnl Solaris 2.5.1, 2.6 has sched_yield in libposix4, not librt. + AC_CHECK_LIB(posix4, sched_yield, [LIBSCHED=-lposix4])]) +fi +AC_SUBST([LIBSCHED]) + +Makefile.am: +TESTS += test-cond +check_PROGRAMS += test-cond +test_cond_LDADD = $(LDADD) @LIBMULTITHREAD@ @LIBSCHED@ diff --git a/modules/lock-tests b/modules/lock-tests index 7c72c94..94876a2 100644 --- a/modules/lock-tests +++ b/modules/lock-tests @@ -2,6 +2,7 @@ Files: tests/test-lock.c Depends-on: +thread configure.ac: dnl Checks for special libraries for the tests/test-lock test. diff --git a/modules/thread b/modules/thread new file mode 100644 index 0000000..c5ee8f3 --- /dev/null +++ b/modules/thread @@ -0,0 +1,25 @@ +Description: +Locking in multithreaded situations. + +Files: +lib/glthread/thread.h +m4/thread.m4 + +Depends-on: +lock + +configure.ac: +gl_THREAD + +Makefile.am: +lib_SOURCES += glthread/glthread.h + +Include: +"glthread/glthread.h" + +License: +LGPLv2+ + +Maintainer: +Yoann Vandoorselaere + diff --git a/tests/test-cond.c b/tests/test-cond.c new file mode 100644 index 0000000..114cf28 --- /dev/null +++ b/tests/test-cond.c @@ -0,0 +1,269 @@ +/* Test of condition in multithreaded situations. + Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#if USE_POSIX_THREADS || USE_SOLARIS_THREADS || USE_PTH_THREADS || USE_WIN32_THREADS + +#if USE_POSIX_THREADS +# define TEST_POSIX_THREADS 1 +#endif +#if USE_SOLARIS_THREADS +# define TEST_SOLARIS_THREADS 1 +#endif +#if USE_PTH_THREADS +# define TEST_PTH_THREADS 1 +#endif +#if USE_WIN32_THREADS +# define TEST_WIN32_THREADS 1 +#endif + +/* Whether to enable locking. + Uncomment this to get a test program without locking, to verify that + it crashes. */ +#define ENABLE_LOCKING 1 + +/* Which tests to perform. + Uncomment some of these, to verify that all tests crash if no locking + is enabled. */ +#define DO_TEST_COND 1 +#define DO_TEST_TIMEDCOND 1 + + +/* Whether to help the scheduler through explicit yield(). + Uncomment this to see if the operating system has a fair scheduler. */ +#define EXPLICIT_YIELD 1 + +/* Whether to print debugging messages. */ +#define ENABLE_DEBUGGING 0 + +#include +#include +#include + +#if !ENABLE_LOCKING +# undef USE_POSIX_THREADS +# undef USE_SOLARIS_THREADS +# undef USE_PTH_THREADS +# undef USE_WIN32_THREADS +#endif + +#include "glthread/thread.h" +#include "glthread/cond.h" +#include "glthread/lock.h" + +#if ENABLE_DEBUGGING +# define dbgprintf printf +#else +# define dbgprintf if (0) printf +#endif + +#if TEST_POSIX_THREADS +# include +static inline void +gl_thread_yield (void) +{ + sched_yield (); +} +#endif + +#if TEST_PTH_THREADS +# include +static inline void +gl_thread_yield (void) +{ + pth_yield (NULL); +} +#endif + +#if TEST_SOLARIS_THREADS +# include +static inline void +gl_thread_yield (void) +{ + thr_yield (); +} +#endif + +#if TEST_WIN32_THREADS +static inline void +gl_thread_yield (void) +{ + Sleep (0); +} +#endif + +#if EXPLICIT_YIELD +# define yield() gl_thread_yield () +#else +# define yield() +#endif + + +/* + * Condition check + */ +#include +static int cond_value = 0; +static gl_cond_t condtest = gl_cond_initializer; +static gl_lock_t lockcond = gl_lock_initializer; + +static void * +cond_routine (void *arg) +{ + gl_lock_lock (lockcond); + while (!cond_value) + { + gl_cond_wait (condtest, lockcond); + } + gl_lock_unlock (lockcond); + + cond_value = 2; + + return NULL; +} + +void +test_cond () +{ + int remain = 2; + gl_thread_t thread; + + cond_value = 0; + + gl_thread_create (thread, cond_routine, NULL); + do + { + yield (); + remain = sleep (remain); + } + while (remain); + + /* signal condition */ + gl_lock_lock (lockcond); + cond_value = 1; + gl_cond_signal (condtest); + gl_lock_unlock (lockcond); + + gl_thread_join (thread, NULL); + + if (cond_value != 2) + abort (); +} + + +/* + * Timed Condition check + */ +static int cond_timeout; + +static void +get_ts (struct timespec *ts) +{ + struct timeval now; + + gettimeofday (&now, NULL); + + ts->tv_sec = now.tv_sec + 1; + ts->tv_nsec = now.tv_usec * 1000; +} + +static void * +timedcond_routine (void *arg) +{ + int ret; + struct timespec ts; + + gl_lock_lock (lockcond); + while (!cond_value) + { + get_ts (&ts); + ret = glthread_cond_timedwait (&condtest, &lockcond, &ts); + if (ret == ETIMEDOUT) + cond_timeout = 1; + } + gl_lock_unlock (lockcond); + + return NULL; +} + +static void +test_timedcond (void) +{ + int remain = 2; + gl_thread_t thread; + + cond_value = cond_timeout = 0; + + gl_thread_create (thread, timedcond_routine, NULL); + + remain = 2; + do + { + yield (); + remain = sleep (remain); + } + while (remain); + + /* signal condition */ + gl_lock_lock (lockcond); + cond_value = 1; + gl_cond_signal (condtest); + gl_lock_unlock (lockcond); + + gl_thread_join (thread, NULL); + + if (!cond_timeout) + abort (); +} + +int +main () +{ +#if TEST_PTH_THREADS + if (!pth_init ()) + abort (); +#endif + +#if DO_TEST_COND + printf ("Starting test_cond ..."); + fflush (stdout); + test_cond (); + printf (" OK\n"); + fflush (stdout); +#endif +#if DO_TEST_TIMEDCOND + printf ("Starting test_timedcond ..."); + fflush (stdout); + test_timedcond (); + printf (" OK\n"); + fflush (stdout); +#endif + + return 0; +} + +#else + +/* No multithreading available. */ + +int +main () +{ + return 77; +} + +#endif diff --git a/tests/test-lock.c b/tests/test-lock.c index 6d2278e..574b45a 100644 --- a/tests/test-lock.c +++ b/tests/test-lock.c @@ -45,6 +45,9 @@ #define DO_TEST_RWLOCK 1 #define DO_TEST_RECURSIVE_LOCK 1 #define DO_TEST_ONCE 1 +#define DO_TEST_COND 1 +#define DO_TEST_TIMEDCOND 1 + /* Whether to help the scheduler through explicit yield(). Uncomment this to see if the operating system has a fair scheduler. */ @@ -71,6 +74,8 @@ # undef USE_PTH_THREADS # undef USE_WIN32_THREADS #endif + +#include "glthread/thread.h" #include "glthread/lock.h" #if ENABLE_DEBUGGING @@ -80,80 +85,29 @@ #endif #if TEST_POSIX_THREADS -# include # include -typedef pthread_t gl_thread_t; -static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) -{ - pthread_t thread; - if (pthread_create (&thread, NULL, func, arg) != 0) - abort (); - return thread; -} -static inline void gl_thread_join (gl_thread_t thread) -{ - void *retval; - if (pthread_join (thread, &retval) != 0) - abort (); -} static inline void gl_thread_yield (void) { sched_yield (); } -static inline void * gl_thread_self (void) -{ - return (void *) pthread_self (); -} #endif + #if TEST_PTH_THREADS # include -typedef pth_t gl_thread_t; -static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) -{ - pth_t thread = pth_spawn (NULL, func, arg); - if (thread == NULL) - abort (); - return thread; -} -static inline void gl_thread_join (gl_thread_t thread) -{ - if (!pth_join (thread, NULL)) - abort (); -} static inline void gl_thread_yield (void) { pth_yield (NULL); } -static inline void * gl_thread_self (void) -{ - return pth_self (); -} #endif + #if TEST_SOLARIS_THREADS # include -typedef thread_t gl_thread_t; -static inline gl_thread_t gl_thread_create (void * (*func) (void *), void *arg) -{ - thread_t thread; - if (thr_create (NULL, 0, func, arg, 0, &thread) != 0) - abort (); - return thread; -} -static inline void gl_thread_join (gl_thread_t thread) -{ - void *retval; - if (thr_join (thread, NULL, &retval) != 0) - abort (); -} static inline void gl_thread_yield (void) { thr_yield (); } -static inline void * gl_thread_self (void) -{ - return (void *) thr_self (); -} #endif + #if TEST_WIN32_THREADS # include typedef HANDLE gl_thread_t; @@ -244,9 +198,9 @@ lock_mutator_thread (void *arg) { int i1, i2, value; - dbgprintf ("Mutator %p before lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before lock\n", glthread_self ()); gl_lock_lock (my_lock); - dbgprintf ("Mutator %p after lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after lock\n", glthread_self ()); i1 = random_account (); i2 = random_account (); @@ -254,20 +208,20 @@ lock_mutator_thread (void *arg) account[i1] += value; account[i2] -= value; - dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before unlock\n", glthread_self ()); gl_lock_unlock (my_lock); - dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after unlock\n", glthread_self ()); - dbgprintf ("Mutator %p before check lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before check lock\n", glthread_self ()); gl_lock_lock (my_lock); check_accounts (); gl_lock_unlock (my_lock); - dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after check unlock\n", glthread_self ()); yield (); } - dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); + dbgprintf ("Mutator %p dying.\n", glthread_self ()); return NULL; } @@ -278,16 +232,16 @@ lock_checker_thread (void *arg) { while (!lock_checker_done) { - dbgprintf ("Checker %p before check lock\n", gl_thread_self ()); + dbgprintf ("Checker %p before check lock\n", glthread_self ()); gl_lock_lock (my_lock); check_accounts (); gl_lock_unlock (my_lock); - dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); + dbgprintf ("Checker %p after check unlock\n", glthread_self ()); yield (); } - dbgprintf ("Checker %p dying.\n", gl_thread_self ()); + dbgprintf ("Checker %p dying.\n", glthread_self ()); return NULL; } @@ -304,15 +258,15 @@ test_lock (void) lock_checker_done = 0; /* Spawn the threads. */ - checkerthread = gl_thread_create (lock_checker_thread, NULL); + gl_thread_create (checkerthread, lock_checker_thread, NULL); for (i = 0; i < THREAD_COUNT; i++) - threads[i] = gl_thread_create (lock_mutator_thread, NULL); + gl_thread_create (threads[i], lock_mutator_thread, NULL); /* Wait for the threads to terminate. */ for (i = 0; i < THREAD_COUNT; i++) - gl_thread_join (threads[i]); + gl_thread_join (threads[i], NULL); lock_checker_done = 1; - gl_thread_join (checkerthread); + gl_thread_join (checkerthread, NULL); check_accounts (); } @@ -331,9 +285,9 @@ rwlock_mutator_thread (void *arg) { int i1, i2, value; - dbgprintf ("Mutator %p before wrlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before wrlock\n", glthread_self ()); gl_rwlock_wrlock (my_rwlock); - dbgprintf ("Mutator %p after wrlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after wrlock\n", glthread_self ()); i1 = random_account (); i2 = random_account (); @@ -341,14 +295,14 @@ rwlock_mutator_thread (void *arg) account[i1] += value; account[i2] -= value; - dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before unlock\n", glthread_self ()); gl_rwlock_unlock (my_rwlock); - dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after unlock\n", glthread_self ()); yield (); } - dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); + dbgprintf ("Mutator %p dying.\n", glthread_self ()); return NULL; } @@ -359,16 +313,16 @@ rwlock_checker_thread (void *arg) { while (!rwlock_checker_done) { - dbgprintf ("Checker %p before check rdlock\n", gl_thread_self ()); + dbgprintf ("Checker %p before check rdlock\n", glthread_self ()); gl_rwlock_rdlock (my_rwlock); check_accounts (); gl_rwlock_unlock (my_rwlock); - dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); + dbgprintf ("Checker %p after check unlock\n", glthread_self ()); yield (); } - dbgprintf ("Checker %p dying.\n", gl_thread_self ()); + dbgprintf ("Checker %p dying.\n", glthread_self ()); return NULL; } @@ -386,16 +340,16 @@ test_rwlock (void) /* Spawn the threads. */ for (i = 0; i < THREAD_COUNT; i++) - checkerthreads[i] = gl_thread_create (rwlock_checker_thread, NULL); + gl_thread_create (checkerthreads[i], rwlock_checker_thread, NULL); for (i = 0; i < THREAD_COUNT; i++) - threads[i] = gl_thread_create (rwlock_mutator_thread, NULL); + gl_thread_create (threads[i], rwlock_mutator_thread, NULL); /* Wait for the threads to terminate. */ for (i = 0; i < THREAD_COUNT; i++) - gl_thread_join (threads[i]); + gl_thread_join (threads[i], NULL); rwlock_checker_done = 1; for (i = 0; i < THREAD_COUNT; i++) - gl_thread_join (checkerthreads[i]); + gl_thread_join (checkerthreads[i], NULL); check_accounts (); } @@ -410,9 +364,9 @@ recshuffle (void) { int i1, i2, value; - dbgprintf ("Mutator %p before lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before lock\n", glthread_self ()); gl_recursive_lock_lock (my_reclock); - dbgprintf ("Mutator %p after lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after lock\n", glthread_self ()); i1 = random_account (); i2 = random_account (); @@ -424,9 +378,9 @@ recshuffle (void) if (((unsigned int) rand() >> 3) % 2) recshuffle (); - dbgprintf ("Mutator %p before unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before unlock\n", glthread_self ()); gl_recursive_lock_unlock (my_reclock); - dbgprintf ("Mutator %p after unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after unlock\n", glthread_self ()); } static void * @@ -438,16 +392,16 @@ reclock_mutator_thread (void *arg) { recshuffle (); - dbgprintf ("Mutator %p before check lock\n", gl_thread_self ()); + dbgprintf ("Mutator %p before check lock\n", glthread_self ()); gl_recursive_lock_lock (my_reclock); check_accounts (); gl_recursive_lock_unlock (my_reclock); - dbgprintf ("Mutator %p after check unlock\n", gl_thread_self ()); + dbgprintf ("Mutator %p after check unlock\n", glthread_self ()); yield (); } - dbgprintf ("Mutator %p dying.\n", gl_thread_self ()); + dbgprintf ("Mutator %p dying.\n", glthread_self ()); return NULL; } @@ -458,16 +412,16 @@ reclock_checker_thread (void *arg) { while (!reclock_checker_done) { - dbgprintf ("Checker %p before check lock\n", gl_thread_self ()); + dbgprintf ("Checker %p before check lock\n", glthread_self ()); gl_recursive_lock_lock (my_reclock); check_accounts (); gl_recursive_lock_unlock (my_reclock); - dbgprintf ("Checker %p after check unlock\n", gl_thread_self ()); + dbgprintf ("Checker %p after check unlock\n", glthread_self ()); yield (); } - dbgprintf ("Checker %p dying.\n", gl_thread_self ()); + dbgprintf ("Checker %p dying.\n", glthread_self ()); return NULL; } @@ -484,15 +438,15 @@ test_recursive_lock (void) reclock_checker_done = 0; /* Spawn the threads. */ - checkerthread = gl_thread_create (reclock_checker_thread, NULL); + gl_thread_create (checkerthread, reclock_checker_thread, NULL); for (i = 0; i < THREAD_COUNT; i++) - threads[i] = gl_thread_create (reclock_mutator_thread, NULL); + gl_thread_create (threads[i], reclock_mutator_thread, NULL); /* Wait for the threads to terminate. */ for (i = 0; i < THREAD_COUNT; i++) - gl_thread_join (threads[i]); + gl_thread_join (threads[i], NULL); reclock_checker_done = 1; - gl_thread_join (checkerthread); + gl_thread_join (checkerthread, NULL); check_accounts (); } @@ -533,10 +487,10 @@ once_contender_thread (void *arg) gl_lock_unlock (ready_lock[id]); if (repeat == REPEAT_COUNT) - break; + break; dbgprintf ("Contender %p waiting for signal for round %d\n", - gl_thread_self (), repeat); + glthread_self (), repeat); #if ENABLE_LOCKING /* Wait for the signal to go. */ gl_rwlock_rdlock (fire_signal[repeat]); @@ -545,10 +499,10 @@ once_contender_thread (void *arg) #else /* Wait for the signal to go. */ while (fire_signal_state <= repeat) - yield (); + yield (); #endif dbgprintf ("Contender %p got the signal for round %d\n", - gl_thread_self (), repeat); + glthread_self (), repeat); /* Contend for execution. */ gl_once (once_control, once_execute); @@ -582,37 +536,37 @@ test_once (void) /* Spawn the threads. */ for (i = 0; i < THREAD_COUNT; i++) - threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i); + gl_thread_create (threads[i], once_contender_thread, (void *) (long) i); for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) { /* Wait until every thread is ready. */ dbgprintf ("Main thread before synchonizing for round %d\n", repeat); for (;;) - { - int ready_count = 0; - for (i = 0; i < THREAD_COUNT; i++) - { - gl_lock_lock (ready_lock[i]); - ready_count += ready[i]; - gl_lock_unlock (ready_lock[i]); - } - if (ready_count == THREAD_COUNT) - break; - yield (); - } + { + int ready_count = 0; + for (i = 0; i < THREAD_COUNT; i++) + { + gl_lock_lock (ready_lock[i]); + ready_count += ready[i]; + gl_lock_unlock (ready_lock[i]); + } + if (ready_count == THREAD_COUNT) + break; + yield (); + } dbgprintf ("Main thread after synchonizing for round %d\n", repeat); if (repeat > 0) - { - /* Check that exactly one thread executed the once_execute() - function. */ - if (performed != 1) - abort (); - } + { + /* Check that exactly one thread executed the once_execute() + function. */ + if (performed != 1) + abort (); + } if (repeat == REPEAT_COUNT) - break; + break; /* Preparation for the next round: Initialize once_control. */ memcpy (&once_control, &fresh_once, sizeof (gl_once_t)); @@ -622,11 +576,11 @@ test_once (void) /* Preparation for the next round: Reset the ready flags. */ for (i = 0; i < THREAD_COUNT; i++) - { - gl_lock_lock (ready_lock[i]); - ready[i] = 0; - gl_lock_unlock (ready_lock[i]); - } + { + gl_lock_lock (ready_lock[i]); + ready[i] = 0; + gl_lock_unlock (ready_lock[i]); + } /* Signal all threads simultaneously. */ dbgprintf ("Main thread giving signal for round %d\n", repeat); @@ -639,7 +593,7 @@ test_once (void) /* Wait for the threads to terminate. */ for (i = 0; i < THREAD_COUNT; i++) - gl_thread_join (threads[i]); + gl_thread_join (threads[i], NULL); } int