Le vendredi 08 août 2008 à 00:37 +0200, Bruno Haible a écrit :
> > - 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).
> 
> It's a pity, because one cannot write a reasonable test program that works
> with GNU Pth without putting sched_yield calls here and there. But I guess
> you're right, and it needs a 5th modules, because of the extra library
> dependency.
> 
> > - Implement *basic* unit test for glthread_cond_wait() /
> > glthread_cond_timedwait().
> 
> I would prefer if you could put this into a separate test-cond.c file -
> so that test-lock.c does not need to depend on the 'cond' module.

Included in the attached patch.

> gl_thread_sigmask ?! Sounds a bit advanced. I'm not sure this can be
> implemented in a portable way, especially on Win32.

Sound like a fairly important function for application that need to
block certain signals in specific threads. I guess we can use
sigprocmask() on systems where the function is not available.


> gl_thread_atfork ?! Sounds advanced and weird to implement as well.

This one is especially important for library using threads: if an
application fork, the library won't be able to recover without this
function. 

Unfortunately, there are still systems where it is not available.

> I propose to clearly document that these two functions are not supported
> on all platforms.

I especially agree concerning pthread_atfork() (I think
pthread_sigmask() should be available on most architecture).


> Now nitpicking:

Should be fixed now.

-- 
Yoann Vandoorselaere <[EMAIL PROTECTED]>
diff --git a/lib/glcond.h b/lib/glcond.h
new file mode 100644
index 0000000..05af610
--- /dev/null
+++ b/lib/glcond.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 <[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 glthread_cond_destroy(COND) 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 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..9316b77
--- /dev/null
+++ b/lib/glthread.h
@@ -0,0 +1,277 @@
+/* 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_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/glcond.m4 b/m4/glcond.m4
new file mode 100644
index 0000000..b98d51e
--- /dev/null
+++ b/m4/glcond.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/glthread.m4 b/m4/glthread.m4
new file mode 100644
index 0000000..43bd94d
--- /dev/null
+++ b/m4/glthread.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/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/glcond-tests b/modules/glcond-tests
new file mode 100644
index 0000000..9ff44a0
--- /dev/null
+++ b/modules/glcond-tests
@@ -0,0 +1,22 @@
+Files:
+tests/test-glcond.c
+
+Depends-on:
+glthread
+
+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-glcond
+check_PROGRAMS += test-glcond
+test_glcond_LDADD = $(LDADD) @LIBMULTITHREAD@ @LIBSCHED@
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..e79945d 100644
--- a/modules/lock-tests
+++ b/modules/lock-tests
@@ -2,6 +2,7 @@ Files:
 tests/test-lock.c
 
 Depends-on:
+glthread
 
 configure.ac:
 dnl Checks for special libraries for the tests/test-lock test.
diff --git a/tests/test-glcond.c b/tests/test-glcond.c
new file mode 100644
index 0000000..aaac4df
--- /dev/null
+++ b/tests/test-glcond.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 <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !ENABLE_LOCKING
+# undef USE_POSIX_THREADS
+# undef USE_SOLARIS_THREADS
+# undef USE_PTH_THREADS
+# undef USE_WIN32_THREADS
+#endif
+
+#include "glthread.h"
+#include "glcond.h"
+#include "lock.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if TEST_POSIX_THREADS
+# include <sched.h>
+static inline void
+gl_thread_yield (void)
+{
+  sched_yield ();
+}
+#endif
+
+#if TEST_PTH_THREADS
+# include <pth.h>
+static inline void
+gl_thread_yield (void)
+{
+  pth_yield (NULL);
+}
+#endif
+
+#if TEST_SOLARIS_THREADS
+# include <thread.h>
+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 <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;
+}
+
+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 2d10833..a620418 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,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

Reply via email to