Le jeudi 07 août 2008 à 11:15 +0200, Bruno Haible a écrit :
> > Attached is an initial implementation of the glcond module, based on
> > your lock module. 
> 
> I like about it that its API is in the same spirit as 'lock' and 'tls'.
> 
> A unit test and a Woe32 implementation are still missing.
> 
> The prefix 'gl' for the module and header file is probably unnecessary:
> most modules are gnulib modules anyway, and if the header was called "cond.h",
> there would not be much risk of collision either.
> 
> The comments still talk about "Locking" and me as the author.
> 
> I would also mention in a comment the distinction between "condition variable"
> and "wait queue" done in [1].

Attached is an updated patch:

- Comments have been updated following your suggestion.

- I didn't modified the prefix yet, waiting for your reply to my
previous mail.

- Include a new glthread module (which doesn't handle sched_yield, since
that might pull another dependency in. I guess we need yet another
separate module for this one).

- Implement *basic* unit test for glthread_cond_wait() /
glthread_cond_timedwait().

- Correct multiples condition bug with the PTH backend.


Regards,

diff --git a/lib/glcond.h b/lib/glcond.h
new file mode 100644
index 0000000..181ec4a
--- /dev/null
+++ b/lib/glcond.h
@@ -0,0 +1,323 @@
+/* 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 <[EMAIL PROTECTED]>, 2008.
+   Based on Bruno Haible <[EMAIL PROTECTED]> 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 <errno.h>
+#include "lock.h"
+
+/* ========================================================================= */
+
+#if USE_POSIX_THREADS
+
+/* Use the POSIX threads library.  */
+
+# include <pthread.h>
+# include <stdlib.h>
+
+# 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 <pth.h>
+# include <stdlib.h>
+
+# 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 glhread_cond_destroy(COND) \
+    (void)
+
+#endif
+
+/* ========================================================================= */
+
+#if USE_SOLARIS_THREADS
+
+/* Use the old Solaris threads library.  */
+
+# include <thread.h>
+# include <synch.h>
+# include <stdlib.h>
+
+# 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.h b/lib/glthread.h
new file mode 100644
index 0000000..f0e59f1
--- /dev/null
+++ b/lib/glthread.h
@@ -0,0 +1,274 @@
+/* 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 <[EMAIL PROTECTED]>, 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 <errno.h>
+
+/* ========================================================================= */
+
+#if USE_POSIX_THREADS
+
+/* Use the POSIX threads library.  */
+
+# include <pthread.h>
+# include <sched.h>
+# include <stdlib.h>
+
+# 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 <pth.h>
+# include <stdlib.h>
+
+# 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 <thread.h>
+# include <synch.h>
+# include <stdlib.h>
+
+# 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(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(&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/glcond.m4 b/m4/glcond.m4
new file mode 100644
index 0000000..b31d6ae
--- /dev/null
+++ b/m4/glcond.m4
@@ -0,0 +1,10 @@
+# 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_REQUIRE([gl_LOCK])
+])
diff --git a/modules/glcond b/modules/glcond
new file mode 100644
index 0000000..6bac3be
--- /dev/null
+++ b/modules/glcond
@@ -0,0 +1,25 @@
+Description:
+Condition in multithreaded situations.
+
+Files:
+lib/glcond.h
+m4/glcond.m4
+
+Depends-on:
+lock
+
+configure.ac:
+gl_COND
+
+Makefile.am:
+lib_SOURCES += glcond.h
+
+Include:
+"glcond.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+Yoann Vandoorselaere
+
diff --git a/modules/glthread b/modules/glthread
new file mode 100644
index 0000000..1cc4b30
--- /dev/null
+++ b/modules/glthread
@@ -0,0 +1,22 @@
+Description:
+Locking in multithreaded situations.
+
+Files:
+lib/glthread.h
+m4/glthread.m4
+
+configure.ac:
+gl_THREAD
+
+Makefile.am:
+lib_SOURCES += glthread.h 
+
+Include:
+"glthread.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+Yoann Vandoorselaere
+
diff --git a/modules/lock-tests b/modules/lock-tests
index 7c72c94..68bc074 100644
--- a/modules/lock-tests
+++ b/modules/lock-tests
@@ -2,6 +2,8 @@ Files:
 tests/test-lock.c
 
 Depends-on:
+glthread
+glcond
 
 configure.ac:
 dnl Checks for special libraries for the tests/test-lock test.
diff --git a/tests/test-lock.c b/tests/test-lock.c
index 2d10833..04ebb1c 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,9 @@
 # undef USE_PTH_THREADS
 # undef USE_WIN32_THREADS
 #endif
+
+#include "glthread.h"
+#include "glcond.h"
 #include "lock.h"
 
 #if ENABLE_DEBUGGING
@@ -80,80 +86,29 @@
 #endif
 
 #if TEST_POSIX_THREADS
-# include <pthread.h>
 # include <sched.h>
-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 <pth.h>
-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 <thread.h>
-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 <windows.h>
 typedef HANDLE gl_thread_t;
@@ -244,9 +199,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 +209,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 +233,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 +259,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 +286,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 +296,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 +314,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 +341,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 +365,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 +379,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 +393,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 +413,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 +439,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 +488,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 +500,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 +537,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 +577,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 +594,117 @@ 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);
+}
+
+/*
+ * Condition check
+ */
+#include <unistd.h>
+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;
+}
+
+void
+test_timedcond()
+{
+  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
@@ -670,6 +735,16 @@ main ()
   test_once ();
   printf (" OK\n"); fflush (stdout);
 #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;
 }

Reply via email to