bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* ISO C 11 threads
@ 2019-06-20 10:40 Bruno Haible
  2019-06-20 10:42 ` [PATCH] ISO C 11 threads preparations Bruno Haible
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-20 10:40 UTC (permalink / raw)
  To: bug-gnulib

Hi,

We are now 8 years past ISO C 11, and still few systems have the ISO C 11 [1]
<threads.h>. This is a multithreading facility [2] that very much resembles
POSIX threading. The differences are:
  - Different header file: <threads.h> instead of <pthread.h>.
  - Different types: thrd_t instead of pthread_t etc.
  - Different function names (thrd_*, mtx_*, call_once, cnd_*, tss_*) instead
    pthread_*.
  - No "advanced" features, just the basic functionality.
  - Mutexes work only in a single process, not across fork()ed processes.
  - No equivalent of 'pthread_sigmask'.
  - It adds a 'thread_local' storage class, that corresponds to GCC's
    '__thread'.
  - The exit value of a thread is an 'int' instead of a 'void *'. (I think
    this is meant to ease portability to Windows [3][4].)
  - Locks/mutexes: There are only plain and recursive locks, no read-write
    locks and no spin locks. The type of a lock (plain vs. recursive and
    whether it supports waiting with a timeout) has to be specified when the
    lock is created. I think this is a recognition of the fact that the
    implementation of a recursive lock or a lock that supports waiting is
    more complex than a the implementation of a plain lock.

So far this facility is implemented in
  - glibc >= 2.29,
  - FreeBSD >= 10,
  - illumos,
  - Solaris >= 11.4 (with a bug in thrd_join),
  - AIX >= 7.1 (with terrible bugs in thrd_create and thrd_join).

To make it portably usable, I'm adding support for this facility to gnulib,
for all systems with POSIX or Windows threads.

No support for older systems, namely Minix 3.1.8, HP-UX 11.11, IRIX 5.3,
Solaris 2.4, BeOS.

Gnulib can only provide the types and functions. The 'thread_local' storage
class is something that requires compiler and linker support; we cannot
provide this portably. Applications have to use the tss_* functions instead
(just like with GCC, you need pthread_get/setspecific when '__thread' is not
available).

On native Windows, this implementation uses native Windows threads, not the
mingw pthreads emulation. This is unlike the gnulib-invented 'thread', 'lock',
'cond', 'tls' modules, which by a historical accident (the way threadlib.m4
was written) use mingw pthreads when available and not disabled through
gl_AVOID_WINPTHREAD or --enable-threads=windows.

[1] https://en.wikipedia.org/wiki/C11_(C_standard_revision)
[2] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
[3] https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-exitthread
[4] https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodethread



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH] ISO C 11 threads preparations
  2019-06-20 10:40 ISO C 11 threads Bruno Haible
@ 2019-06-20 10:42 ` Bruno Haible
  2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-20 10:42 UTC (permalink / raw)
  To: bug-gnulib

[-- Attachment #1: Type: text/plain, Size: 1921 bytes --]

The first patch series are preparations:

01  lock tests: Avoid reference to undefined variable if !ENABLE_LOCKING.
02  cond tests: Simplify.
03  tls tests: Small improvements.

These fix minor nits I found while looking at the tests.

04  thread, lock, cond, tls: Recognize C11 multithreaded applications.

These gnulib modules have optimizations for single-threaded applications.
They recognizes a single-threaded application by the fact that libpthread
is not linked in (when pthread_create exists only in libpthread, not in
libc). But on FreeBSD, the ISO C 11 threads implementation is in
libstdthreads, not libpthread. Without this fix, the locks from the 'lock'
module would not work in programs that create their threads through
thrd_create instead of pthread_create.

05  pthread_mutex_timedlock: New module.

This is an emulation of pthread_mutex_timedlock on systems that have
<pthread.h> but lack this particular function.

06  cond: Make glthread_cond_timedwait more reliable on Windows.
07  lock, cond: Avoid possible counter wraparound on Windows.

Small reliability fixes.

08  windows-once: New module.
09  windows-mutex: New module.
10  windows-recmutex: New module.
11  windows-timedmutex: New module.
12  windows-timedrecmutex: New module.
13  windows-cond: New module.
14  windows-tls: New module.
15  windows-thread: New module.

These modules contain the native Windows implementation of the
primitives. Refactored from the thread, lock, cond, tls module,
to avoid code duplication.
- windows-once: refactored from 'lock',
- windows-mutex, windows-recmutex: likewise. Adds support for the
  trylock operations.
- windows-timedmutex, windows-timedrecmutex: These are new lock
  implementations that support waiting with a timeout, based on
  windows-mutex and windows-recmutex.
- windows-cond: refactored from 'cond'.
- windows-tls: refactored from 'tls'.
- windows-thread: refactored from 'thread'.

[-- Attachment #2: 0001-lock-tests-Avoid-reference-to-undefined-variable-if-.patch --]
[-- Type: text/x-patch, Size: 1247 bytes --]

From e8d62a2ae99c44c54c09efd28e39a0e562835125 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 03:50:52 +0200
Subject: [PATCH 01/26] lock tests: Avoid reference to undefined variable if
 !ENABLE_LOCKING.

* tests/test-lock.c (test_once): Don't reference fire_signal if
!ENABLE_LOCKING.
---
 ChangeLog         | 6 ++++++
 tests/test-lock.c | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 96cbd47..bd31e14 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
+	lock tests: Avoid reference to undefined variable if !ENABLE_LOCKING.
+	* tests/test-lock.c (test_once): Don't reference fire_signal if
+	!ENABLE_LOCKING.
+
 2019-06-19  Bruno Haible  <bruno@clisp.org>
 
 	nanosleep: Relicense under LGPLv2+.
diff --git a/tests/test-lock.c b/tests/test-lock.c
index 166f85b..ca7dd62 100644
--- a/tests/test-lock.c
+++ b/tests/test-lock.c
@@ -647,9 +647,11 @@ test_once (void)
   fire_signal_state = 0;
 #endif
 
+#if ENABLE_LOCKING
   /* Block all fire_signals.  */
   for (i = REPEAT_COUNT-1; i >= 0; i--)
     gl_rwlock_wrlock (fire_signal[i]);
+#endif
 
   /* Spawn the threads.  */
   for (i = 0; i < THREAD_COUNT; i++)
-- 
2.7.4


[-- Attachment #3: 0002-cond-tests-Simplify.patch --]
[-- Type: text/x-patch, Size: 1150 bytes --]

From a06d50500ce0a3aa39e3cf1a6b4bf0ed3c1d7c88 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 03:52:28 +0200
Subject: [PATCH 02/26] cond tests: Simplify.

* tests/test-cond.c (test_timedcond): Remove redundant assignment.
---
 ChangeLog         | 5 +++++
 tests/test-cond.c | 2 --
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index bd31e14..9472275 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	cond tests: Simplify.
+	* tests/test-cond.c (test_timedcond): Remove redundant assignment.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	lock tests: Avoid reference to undefined variable if !ENABLE_LOCKING.
 	* tests/test-lock.c (test_once): Don't reference fire_signal if
 	!ENABLE_LOCKING.
diff --git a/tests/test-cond.c b/tests/test-cond.c
index a4c2adc..1de6783 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -151,8 +151,6 @@ test_timedcond (void)
   cond_value = cond_timeout = 0;
 
   thread = gl_thread_create (timedcond_routine, NULL);
-
-  remain = 2;
   do
     {
       yield ();
-- 
2.7.4


[-- Attachment #4: 0003-tls-tests-Small-improvements.patch --]
[-- Type: text/x-patch, Size: 2220 bytes --]

From 97ae5364457c30ea826fa5e6c0c0a41c73a30c38 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 03:53:51 +0200
Subject: [PATCH 03/26] tls tests: Small improvements.

* tests/test-tls.c: Include <stdint.h>.
(worker_thread): Avoid gcc warning on 64-bit mingw.
(test_tls): Pass a different id to each thread.
* modules/tls-tests (Depends-on): Add stdint.
---
 ChangeLog         | 8 ++++++++
 modules/tls-tests | 1 +
 tests/test-tls.c  | 5 +++--
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 9472275..b8ff877 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	tls tests: Small improvements.
+	* tests/test-tls.c: Include <stdint.h>.
+	(worker_thread): Avoid gcc warning on 64-bit mingw.
+	(test_tls): Pass a different id to each thread.
+	* modules/tls-tests (Depends-on): Add stdint.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	cond tests: Simplify.
 	* tests/test-cond.c (test_timedcond): Remove redundant assignment.
 
diff --git a/modules/tls-tests b/modules/tls-tests
index 562e36b..b93f673 100644
--- a/modules/tls-tests
+++ b/modules/tls-tests
@@ -3,6 +3,7 @@ tests/test-tls.c
 
 Depends-on:
 thread
+stdint
 yield
 
 configure.ac:
diff --git a/tests/test-tls.c b/tests/test-tls.c
index 6bfa6ff..0cd6b1d 100644
--- a/tests/test-tls.c
+++ b/tests/test-tls.c
@@ -46,6 +46,7 @@
 /* Number of operations performed in each thread.  */
 #define REPEAT_COUNT 50000
 
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -90,7 +91,7 @@ static gl_tls_key_t mykeys[KEYS_COUNT];
 static void *
 worker_thread (void *arg)
 {
-  unsigned int id = (unsigned int) (unsigned long) arg;
+  unsigned int id = (unsigned int) (uintptr_t) arg;
   int i, j, repeat;
   unsigned int values[KEYS_COUNT];
 
@@ -172,7 +173,7 @@ test_tls (void)
 
       /* Spawn the threads.  */
       for (i = 0; i < THREAD_COUNT; i++)
-        threads[i] = gl_thread_create (worker_thread, NULL);
+        threads[i] = gl_thread_create (worker_thread, (void *) (uintptr_t) i);
 
       /* Wait for the threads to terminate.  */
       for (i = 0; i < THREAD_COUNT; i++)
-- 
2.7.4


[-- Attachment #5: 0004-thread-lock-cond-tls-Recognize-C11-multithreaded-app.patch --]
[-- Type: text/x-patch, Size: 8481 bytes --]

From 79c2545308000d2120009a76bea4f36f06f16b1b Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 03:54:56 +0200
Subject: [PATCH 04/26] thread, lock, cond, tls: Recognize C11 multithreaded
 applications.

* m4/threadlib.m4 (gl_THREADLIB_BODY): Test for <threads.h>.
* lib/glthread/thread.h (c11_threads_in_use): New macro.
(pthread_in_use, pth_in_use, thread_in_use): Use it.
* lib/glthread/lock.h (c11_threads_in_use): New macro.
(pthread_in_use, pth_in_use, thread_in_use): Use it.
* lib/glthread/cond.h (c11_threads_in_use): New macro.
(pthread_in_use, pth_in_use, thread_in_use): Use it.
* lib/glthread/tls.h (c11_threads_in_use): New macro.
(pthread_in_use, pth_in_use, thread_in_use): Use it.
---
 ChangeLog             | 13 +++++++++++++
 lib/glthread/cond.h   | 18 +++++++++++++++---
 lib/glthread/lock.h   | 17 ++++++++++++++---
 lib/glthread/thread.h | 17 ++++++++++++++---
 lib/glthread/tls.h    | 17 ++++++++++++++---
 m4/threadlib.m4       |  9 ++++++++-
 6 files changed, 78 insertions(+), 13 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index b8ff877..a9fe91e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	thread, lock, cond, tls: Recognize C11 multithreaded applications.
+	* m4/threadlib.m4 (gl_THREADLIB_BODY): Test for <threads.h>.
+	* lib/glthread/thread.h (c11_threads_in_use): New macro.
+	(pthread_in_use, pth_in_use, thread_in_use): Use it.
+	* lib/glthread/lock.h (c11_threads_in_use): New macro.
+	(pthread_in_use, pth_in_use, thread_in_use): Use it.
+	* lib/glthread/cond.h (c11_threads_in_use): New macro.
+	(pthread_in_use, pth_in_use, thread_in_use): Use it.
+	* lib/glthread/tls.h (c11_threads_in_use): New macro.
+	(pthread_in_use, pth_in_use, thread_in_use): Use it.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	tls tests: Small improvements.
 	* tests/test-tls.c: Include <stdint.h>.
 	(worker_thread): Avoid gcc warning on 64-bit mingw.
diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h
index abfa753..6540f1d 100644
--- a/lib/glthread/cond.h
+++ b/lib/glthread/cond.h
@@ -54,6 +54,17 @@
 #include <time.h>
 
 #include "glthread/lock.h"
+
+#if !defined c11_threads_in_use
+# if HAVE_THREADS_H && (USE_POSIX_THREADS_WEAK || USE_PTH_THREADS_WEAK || USE_SOLARIS_THREADS_WEAK)
+#  include <threads.h>
+#  pragma weak thrd_exit
+#  define c11_threads_in_use() (thrd_exit != NULL)
+# else
+#  define c11_threads_in_use() 0
+# endif
+#endif
+
 #ifndef _GL_INLINE_HEADER_BEGIN
  #error "Please include config.h first."
 #endif
@@ -115,7 +126,8 @@ extern int glthread_in_use (void);
 
 #  if !PTHREAD_IN_USE_DETECTION_HARD
 #   pragma weak pthread_mutexattr_gettype
-#   define pthread_in_use() (pthread_mutexattr_gettype != NULL)
+#   define pthread_in_use() \
+      (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
 #  endif
 
 # else
@@ -177,7 +189,7 @@ extern "C" {
 #  pragma weak pth_timeout
 
 #  pragma weak pth_cancel
-#  define pth_in_use() (pth_cancel != NULL)
+#  define pth_in_use() (pth_cancel != NULL || c11_threads_in_use ())
 
 # else
 
@@ -237,7 +249,7 @@ extern "C" {
 #  pragma weak cond_broadcast
 #  pragma weak cond_destroy
 #  pragma weak thr_suspend
-#  define thread_in_use() (thr_suspend != NULL)
+#  define thread_in_use() (thr_suspend != NULL || c11_threads_in_use ())
 
 # else
 
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 6e1fdf8..cc6fb5e 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -81,6 +81,16 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#if !defined c11_threads_in_use
+# if HAVE_THREADS_H && (USE_POSIX_THREADS_WEAK || USE_PTH_THREADS_WEAK || USE_SOLARIS_THREADS_WEAK)
+#  include <threads.h>
+#  pragma weak thrd_exit
+#  define c11_threads_in_use() (thrd_exit != NULL)
+# else
+#  define c11_threads_in_use() 0
+# endif
+#endif
+
 /* ========================================================================= */
 
 #if USE_POSIX_THREADS
@@ -156,7 +166,8 @@ extern int glthread_in_use (void);
          pthread_rwlockattr_init
      */
 #   pragma weak pthread_mutexattr_gettype
-#   define pthread_in_use() (pthread_mutexattr_gettype != NULL)
+#   define pthread_in_use() \
+      (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
 #  endif
 
 # else
@@ -421,7 +432,7 @@ extern "C" {
 #  pragma weak pth_cond_notify
 
 #  pragma weak pth_cancel
-#  define pth_in_use() (pth_cancel != NULL)
+#  define pth_in_use() (pth_cancel != NULL || c11_threads_in_use ())
 
 # else
 
@@ -572,7 +583,7 @@ extern "C" {
 #  pragma weak thr_self
 
 #  pragma weak thr_suspend
-#  define thread_in_use() (thr_suspend != NULL)
+#  define thread_in_use() (thr_suspend != NULL || c11_threads_in_use ())
 
 # else
 
diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h
index 1d2a544..f263129 100644
--- a/lib/glthread/thread.h
+++ b/lib/glthread/thread.h
@@ -74,6 +74,16 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#if !defined c11_threads_in_use
+# if HAVE_THREADS_H && (USE_POSIX_THREADS_WEAK || USE_PTH_THREADS_WEAK || USE_SOLARIS_THREADS_WEAK)
+#  include <threads.h>
+#  pragma weak thrd_exit
+#  define c11_threads_in_use() (thrd_exit != NULL)
+# else
+#  define c11_threads_in_use() 0
+# endif
+#endif
+
 #ifndef _GL_INLINE_HEADER_BEGIN
  #error "Please include config.h first."
 #endif
@@ -148,7 +158,8 @@ extern int glthread_in_use (void);
 
 #  if !PTHREAD_IN_USE_DETECTION_HARD
 #   pragma weak pthread_mutexattr_gettype
-#   define pthread_in_use() (pthread_mutexattr_gettype != NULL)
+#   define pthread_in_use() \
+      (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
 #  endif
 
 # else
@@ -234,7 +245,7 @@ extern "C" {
 #  pragma weak pth_exit
 
 #  pragma weak pth_cancel
-#  define pth_in_use() (pth_cancel != NULL)
+#  define pth_in_use() (pth_cancel != NULL || c11_threads_in_use ())
 
 # else
 
@@ -287,7 +298,7 @@ extern "C" {
 #  pragma weak thr_exit
 
 #  pragma weak thr_suspend
-#  define thread_in_use() (thr_suspend != NULL)
+#  define thread_in_use() (thr_suspend != NULL || c11_threads_in_use ())
 
 # else
 
diff --git a/lib/glthread/tls.h b/lib/glthread/tls.h
index 399a879..ab85409 100644
--- a/lib/glthread/tls.h
+++ b/lib/glthread/tls.h
@@ -46,6 +46,16 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#if !defined c11_threads_in_use
+# if HAVE_THREADS_H && (USE_POSIX_THREADS_WEAK || USE_PTH_THREADS_WEAK || USE_SOLARIS_THREADS_WEAK)
+#  include <threads.h>
+#  pragma weak thrd_exit
+#  define c11_threads_in_use() (thrd_exit != NULL)
+# else
+#  define c11_threads_in_use() 0
+# endif
+#endif
+
 /* ========================================================================= */
 
 #if USE_POSIX_THREADS
@@ -77,7 +87,8 @@ extern int glthread_in_use (void);
 
 #  if !PTHREAD_IN_USE_DETECTION_HARD
 #   pragma weak pthread_mutexattr_gettype
-#   define pthread_in_use() (pthread_mutexattr_gettype != NULL)
+#   define pthread_in_use() \
+      (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
 #  endif
 
 # else
@@ -131,7 +142,7 @@ typedef union
 #  pragma weak pth_key_delete
 
 #  pragma weak pth_cancel
-#  define pth_in_use() (pth_cancel != NULL)
+#  define pth_in_use() (pth_cancel != NULL || c11_threads_in_use ())
 
 # else
 
@@ -183,7 +194,7 @@ typedef union
 #  pragma weak thr_setspecific
 
 #  pragma weak thr_suspend
-#  define thread_in_use() (thr_suspend != NULL)
+#  define thread_in_use() (thr_suspend != NULL || c11_threads_in_use ())
 
 # else
 
diff --git a/m4/threadlib.m4 b/m4/threadlib.m4
index b4401f8..f02eb97 100644
--- a/m4/threadlib.m4
+++ b/m4/threadlib.m4
@@ -1,4 +1,4 @@
-# threadlib.m4 serial 17
+# threadlib.m4 serial 18
 dnl Copyright (C) 2005-2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -160,6 +160,13 @@ int main ()
          *" -static "*) gl_cv_have_weak=no ;;
        esac
       ])
+    if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then
+      dnl If we use weak symbols to implement pthread_in_use / pth_in_use /
+      gnl thread_in_use, we also need to test whether the ISO C 11 thrd_create
+      dnl facility is in use.
+      AC_CHECK_HEADERS_ONCE([threads.h])
+      :
+    fi
     if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
       # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that
       # it groks <pthread.h>. It's added above, in gl_THREADLIB_EARLY_BODY.
-- 
2.7.4


[-- Attachment #6: 0005-pthread_mutex_timedlock-New-module.patch --]
[-- Type: text/x-patch, Size: 13365 bytes --]

From 145e16d89f89e000ff6a4f1892b17e34560f9e96 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:02:52 +0200
Subject: [PATCH 05/26] pthread_mutex_timedlock: New module.

* lib/pthread.in.h (pthread_mutex_timedlock): New dummy function and
new declaration.
* lib/pthread_mutex_timedlock.c: New file.
* m4/pthread_mutex_timedlock.m4: New file.
* m4/pthread.m4 (gl_PTHREAD_CHECK): Don't call AC_LIBOBJ here. Test
whether pthread_mutex_timedlock is declared.
(gl_PTHREAD_MODULE_INDICATOR): New macro.
(gl_PTHREAD_DEFAULTS): Initialize GNULIB_PTHREAD_MUTEX_TIMEDLOCK,
HAVE_PTHREAD_MUTEX_TIMEDLOCK.
* modules/pthread (configure.ac): Call AC_LIBOBJ here.
(Makefile.am): Substitute GNULIB_PTHREAD_MUTEX_TIMEDLOCK,
HAVE_PTHREAD_MUTEX_TIMEDLOCK.
* modules/pthread_mutex_timedlock: New file.
* doc/posix-functions/pthread_mutex_timedlock.texi: Mention the new
module.
---
 ChangeLog                                        | 19 ++++++
 doc/posix-functions/pthread_mutex_timedlock.texi | 10 +--
 lib/pthread.in.h                                 | 26 +++++++
 lib/pthread_mutex_timedlock.c                    | 87 ++++++++++++++++++++++++
 m4/pthread.m4                                    | 32 ++++++---
 m4/pthread_mutex_timedlock.m4                    | 13 ++++
 modules/pthread                                  |  5 ++
 modules/pthread_mutex_timedlock                  | 28 ++++++++
 8 files changed, 207 insertions(+), 13 deletions(-)
 create mode 100644 lib/pthread_mutex_timedlock.c
 create mode 100644 m4/pthread_mutex_timedlock.m4
 create mode 100644 modules/pthread_mutex_timedlock

diff --git a/ChangeLog b/ChangeLog
index a9fe91e..d9afc4e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	pthread_mutex_timedlock: New module.
+	* lib/pthread.in.h (pthread_mutex_timedlock): New dummy function and
+	new declaration.
+	* lib/pthread_mutex_timedlock.c: New file.
+	* m4/pthread_mutex_timedlock.m4: New file.
+	* m4/pthread.m4 (gl_PTHREAD_CHECK): Don't call AC_LIBOBJ here. Test
+	whether pthread_mutex_timedlock is declared.
+	(gl_PTHREAD_MODULE_INDICATOR): New macro.
+	(gl_PTHREAD_DEFAULTS): Initialize GNULIB_PTHREAD_MUTEX_TIMEDLOCK,
+	HAVE_PTHREAD_MUTEX_TIMEDLOCK.
+	* modules/pthread (configure.ac): Call AC_LIBOBJ here.
+	(Makefile.am): Substitute GNULIB_PTHREAD_MUTEX_TIMEDLOCK,
+	HAVE_PTHREAD_MUTEX_TIMEDLOCK.
+	* modules/pthread_mutex_timedlock: New file.
+	* doc/posix-functions/pthread_mutex_timedlock.texi: Mention the new
+	module.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	thread, lock, cond, tls: Recognize C11 multithreaded applications.
 	* m4/threadlib.m4 (gl_THREADLIB_BODY): Test for <threads.h>.
 	* lib/glthread/thread.h (c11_threads_in_use): New macro.
diff --git a/doc/posix-functions/pthread_mutex_timedlock.texi b/doc/posix-functions/pthread_mutex_timedlock.texi
index 888b310..aaa76eb 100644
--- a/doc/posix-functions/pthread_mutex_timedlock.texi
+++ b/doc/posix-functions/pthread_mutex_timedlock.texi
@@ -4,15 +4,17 @@
 
 POSIX specification:@* @url{http://www.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html}
 
-Gnulib module: ---
+Gnulib module: pthread_mutex_timedlock
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+Mac OS X 10.5, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, Cygwin, mingw, MSVC 14, BeOS, Android 4.4.
+But the provided replacement is just a dummy on some of these platforms:
+Minix 3.1.8, HP-UX 11, IRIX 5.3, Solaris 2.4, mingw, MSVC 14, BeOS.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-Mac OS X 10.5, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, Cygwin, mingw, MSVC 14, BeOS, Android 4.4.
 @end itemize
diff --git a/lib/pthread.in.h b/lib/pthread.in.h
index 1deef7d..cd3cf1f 100644
--- a/lib/pthread.in.h
+++ b/lib/pthread.in.h
@@ -252,6 +252,14 @@ pthread_mutex_trylock (pthread_mutex_t *mutex)
 }
 
 _GL_PTHREAD_INLINE int
+pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+  /* There is only one thread, so it always gets the lock.  This
+     implementation does not support PTHREAD_MUTEX_ERRORCHECK.  */
+  return 0;
+}
+
+_GL_PTHREAD_INLINE int
 pthread_mutex_unlock (pthread_mutex_t *mutex)
 {
   /* There is only one thread, so it always unlocks successfully.
@@ -263,6 +271,24 @@ pthread_mutex_unlock (pthread_mutex_t *mutex)
 #  define GNULIB_defined_pthread_functions 1
 # endif
 
+#else
+
+# if @GNULIB_PTHREAD_MUTEX_TIMEDLOCK@
+#  if !@HAVE_PTHREAD_MUTEX_TIMEDLOCK@
+_GL_FUNCDECL_SYS (pthread_mutex_timedlock, int,
+                  (pthread_mutex_t *, const struct timespec *));
+#  endif
+_GL_CXXALIAS_SYS (pthread_mutex_timedlock, int,
+                  (pthread_mutex_t *, const struct timespec *));
+_GL_CXXALIASWARN (pthread_mutex_timedlock);
+# elif defined GNULIB_POSIXCHECK
+#  undef pthread_mutex_timedlock
+#  if HAVE_RAW_DECL_PTHREAD_MUTEX_TIMEDLOCK
+_GL_WARN_ON_USE (pthread_mutex_timedlock, "pthread_mutex_timedlock is unportable - "
+                 "use gnulib module pthread_mutex_timedlock for portability");
+#  endif
+# endif
+
 #endif
 
 #if ! @HAVE_PTHREAD_SPINLOCK_T@
diff --git a/lib/pthread_mutex_timedlock.c b/lib/pthread_mutex_timedlock.c
new file mode 100644
index 0000000..9b6bc63
--- /dev/null
+++ b/lib/pthread_mutex_timedlock.c
@@ -0,0 +1,87 @@
+/* Lock a mutex, abandoning after a certain time.
+   Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <pthread.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime)
+{
+  /* Poll the mutex's state in regular intervals.  Ugh.  */
+  /* POSIX says:
+      "Under no circumstance shall the function fail with a timeout if
+       the mutex can be locked immediately. The validity of the abstime
+       parameter need not be checked if the mutex can be locked
+       immediately."
+     Therefore start the loop with a pthread_mutex_trylock call.  */
+  for (;;)
+    {
+      int err;
+      struct timeval currtime;
+      unsigned long remaining;
+      struct timespec duration;
+
+      err = pthread_mutex_trylock (mutex);
+      if (err != EBUSY)
+        return err;
+
+      gettimeofday (&currtime, NULL);
+
+      if (currtime.tv_sec > abstime->tv_sec)
+        remaining = 0;
+      else
+        {
+          unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+          remaining = seconds * 1000000000;
+          if (remaining / 1000000000 != seconds) /* overflow? */
+            remaining = ULONG_MAX;
+          else
+            {
+              long nanoseconds =
+                abstime->tv_nsec - currtime.tv_usec * 1000;
+              if (nanoseconds >= 0)
+                {
+                  remaining += nanoseconds;
+                  if (remaining < nanoseconds) /* overflow? */
+                    remaining = ULONG_MAX;
+                }
+              else
+                {
+                  if (remaining >= - nanoseconds)
+                    remaining -= (- nanoseconds);
+                  else
+                    remaining = 0;
+                }
+            }
+        }
+      if (remaining == 0)
+        return ETIMEDOUT;
+
+      /* Sleep 1 ms.  */
+      duration.tv_sec = 0;
+      duration.tv_nsec = 1000000;
+      if (duration.tv_nsec > remaining)
+        duration.tv_nsec = remaining;
+      nanosleep (&duration, NULL);
+    }
+}
diff --git a/m4/pthread.m4 b/m4/pthread.m4
index 0a219d7..bccac96 100644
--- a/m4/pthread.m4
+++ b/m4/pthread.m4
@@ -1,4 +1,4 @@
-# pthread.m4 serial 10
+# pthread.m4 serial 11
 dnl Copyright (C) 2009-2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -39,19 +39,22 @@ AC_DEFUN([gl_PTHREAD_CHECK],
      HAVE_PTHREAD_SPINLOCK_T=0
    fi
 
-   if test $ac_cv_header_pthread_h != yes ||
-      test $ac_cv_type_pthread_t != yes ||
-      test $ac_cv_type_pthread_spinlock_t != yes; then
+   if test $HAVE_PTHREAD_H = 0 || test $HAVE_PTHREAD_T = 0 || test $HAVE_PTHREAD_SPINLOCK_T = 0; then
      PTHREAD_H='pthread.h'
-     AC_LIBOBJ([pthread])
    elif test $gl_cv_header_pthread_h_pollution = yes; then
-     PTHREAD_H=pthread.h
+     PTHREAD_H='pthread.h'
    else
      PTHREAD_H=
    fi
    AC_SUBST([PTHREAD_H])
    AM_CONDITIONAL([GL_GENERATE_PTHREAD_H], [test -n "$PTHREAD_H"])
 
+   dnl Check for declarations of anything we want to poison if the
+   dnl corresponding gnulib module is not in use, if it is not common
+   dnl enough to be declared everywhere.
+   gl_WARN_ON_USE_PREPARE([[#include <pthread.h>
+     ]], [pthread_mutex_timedlock])
+
    LIB_PTHREAD=
    if test $ac_cv_header_pthread_h = yes; then
      dnl We cannot use AC_SEARCH_LIBS here, because on OSF/1 5.1 pthread_join
@@ -91,10 +94,21 @@ AC_DEFUN([gl_PTHREAD_CHECK],
    AC_REQUIRE([AC_C_RESTRICT])
 ])
 
+AC_DEFUN([gl_PTHREAD_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_PTHREAD_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
 AC_DEFUN([gl_PTHREAD_DEFAULTS],
 [
+  GNULIB_PTHREAD_MUTEX_TIMEDLOCK=0; AC_SUBST([GNULIB_PTHREAD_MUTEX_TIMEDLOCK])
   dnl Assume proper GNU behavior unless another module says otherwise.
-  HAVE_PTHREAD_H=1;              AC_SUBST([HAVE_PTHREAD_H])
-  HAVE_PTHREAD_T=1;              AC_SUBST([HAVE_PTHREAD_T])
-  HAVE_PTHREAD_SPINLOCK_T=1;     AC_SUBST([HAVE_PTHREAD_SPINLOCK_T])
+  HAVE_PTHREAD_H=1;                 AC_SUBST([HAVE_PTHREAD_H])
+  HAVE_PTHREAD_T=1;                 AC_SUBST([HAVE_PTHREAD_T])
+  HAVE_PTHREAD_SPINLOCK_T=1;        AC_SUBST([HAVE_PTHREAD_SPINLOCK_T])
+  HAVE_PTHREAD_MUTEX_TIMEDLOCK=1;   AC_SUBST([HAVE_PTHREAD_MUTEX_TIMEDLOCK])
 ])
diff --git a/m4/pthread_mutex_timedlock.m4 b/m4/pthread_mutex_timedlock.m4
new file mode 100644
index 0000000..664670f
--- /dev/null
+++ b/m4/pthread_mutex_timedlock.m4
@@ -0,0 +1,13 @@
+# pthread_mutex_timedlock.m4 serial 1
+dnl Copyright (C) 2019 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_FUNC_PTHREAD_MUTEX_TIMEDLOCK],
+[
+  AC_REQUIRE([gl_PTHREAD_DEFAULTS])
+
+  AC_CHECK_DECL([pthread_mutex_timedlock], , [HAVE_PTHREAD_MUTEX_TIMEDLOCK=0],
+    [[#include <pthread.h>]])
+])
diff --git a/modules/pthread b/modules/pthread
index 3fb3920..81e2aad 100644
--- a/modules/pthread
+++ b/modules/pthread
@@ -18,6 +18,9 @@ AC_DEFINE([_THREAD_SAFE], 1, [For thread-safety on AIX, FreeBSD.])
 
 configure.ac:
 gl_PTHREAD_CHECK
+if test $HAVE_PTHREAD_H = 0 || test $HAVE_PTHREAD_T = 0 || test $HAVE_PTHREAD_SPINLOCK_T = 0; then
+  AC_LIBOBJ([pthread])
+fi
 gl_MODULE_INDICATOR([pthread])
 
 Makefile.am:
@@ -35,8 +38,10 @@ pthread.h: pthread.in.h $(top_builddir)/config.status
 	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
 	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
 	      -e 's|@''NEXT_PTHREAD_H''@|$(NEXT_PTHREAD_H)|g' \
+	      -e 's/@''GNULIB_PTHREAD_MUTEX_TIMEDLOCK''@/$(GNULIB_PTHREAD_MUTEX_TIMEDLOCK)/g' \
 	      -e 's|@''HAVE_PTHREAD_T''@|$(HAVE_PTHREAD_T)|g' \
 	      -e 's|@''HAVE_PTHREAD_SPINLOCK_T''@|$(HAVE_PTHREAD_SPINLOCK_T)|g' \
+	      -e 's|@''HAVE_PTHREAD_MUTEX_TIMEDLOCK''@|$(HAVE_PTHREAD_MUTEX_TIMEDLOCK)|g' \
 	      < $(srcdir)/pthread.in.h; \
 	} > $@-t && \
 	mv $@-t $@
diff --git a/modules/pthread_mutex_timedlock b/modules/pthread_mutex_timedlock
new file mode 100644
index 0000000..2ae39f4
--- /dev/null
+++ b/modules/pthread_mutex_timedlock
@@ -0,0 +1,28 @@
+Description:
+Lock a mutex, abandoning after a certain time.
+
+Files:
+lib/pthread_mutex_timedlock.c
+m4/pthread_mutex_timedlock.m4
+
+Depends-on:
+pthread
+nanosleep       [test $HAVE_PTHREAD_T = 1 && test $HAVE_PTHREAD_MUTEX_TIMEDLOCK = 0]
+
+configure.ac:
+gl_FUNC_PTHREAD_MUTEX_TIMEDLOCK
+if test $HAVE_PTHREAD_T = 1 && test $HAVE_PTHREAD_MUTEX_TIMEDLOCK = 0; then
+  AC_LIBOBJ([pthread_mutex_timedlock])
+fi
+gl_PTHREAD_MODULE_INDICATOR([pthread_mutex_timedlock])
+
+Makefile.am:
+
+Include:
+<pthread.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #7: 0006-cond-Make-glthread_cond_timedwait-more-reliable-on-W.patch --]
[-- Type: text/x-patch, Size: 10262 bytes --]

From d4c0a04b4dadf31e4c7c4bab46887528e4157f2b Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:03:37 +0200
Subject: [PATCH 06/26] cond: Make glthread_cond_timedwait more reliable on
 Windows.

* lib/glthread/cond.c (glthread_cond_timedwait_func): Initialize the
condition variable before looking at the current time.
---
 ChangeLog           |   6 ++
 lib/glthread/cond.c | 222 ++++++++++++++++++++++++++--------------------------
 2 files changed, 118 insertions(+), 110 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d9afc4e..5271997 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	cond: Make glthread_cond_timedwait more reliable on Windows.
+	* lib/glthread/cond.c (glthread_cond_timedwait_func): Initialize the
+	condition variable before looking at the current time.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	pthread_mutex_timedlock: New module.
 	* lib/pthread.in.h (pthread_mutex_timedlock): New dummy function and
 	new declaration.
diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c
index 5b8827e..b0304bc 100644
--- a/lib/glthread/cond.c
+++ b/lib/glthread/cond.c
@@ -285,14 +285,6 @@ glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
 int
 glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
 {
-  struct timeval currtime;
-
-  gettimeofday (&currtime, NULL);
-  if (currtime.tv_sec > abstime->tv_sec
-      || (currtime.tv_sec == abstime->tv_sec
-          && currtime.tv_usec * 1000 >= abstime->tv_nsec))
-    return ETIMEDOUT;
-
   if (!cond->guard.done)
     {
       if (InterlockedIncrement (&cond->guard.started) == 0)
@@ -306,110 +298,120 @@ glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec
           Sleep (0);
     }
 
-  EnterCriticalSection (&cond->lock);
   {
-    struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
-    LeaveCriticalSection (&cond->lock);
-    if (elt == NULL)
-      {
-        /* Allocation failure.  Weird.  */
-        return EAGAIN;
-      }
-    else
-      {
-        HANDLE event = elt->event;
-        int err;
-        DWORD timeout;
-        DWORD result;
+    struct timeval currtime;
 
-        /* Now release the lock and let any other thread take it.  */
-        err = glthread_lock_unlock (lock);
-        if (err != 0)
-          {
-            EnterCriticalSection (&cond->lock);
-            gl_waitqueue_remove (&cond->waiters, elt);
-            LeaveCriticalSection (&cond->lock);
-            CloseHandle (event);
-            free (elt);
-            return err;
-          }
-        /* POSIX says:
-            "If another thread is able to acquire the mutex after the
-             about-to-block thread has released it, then a subsequent call to
-             pthread_cond_broadcast() or pthread_cond_signal() in that thread
-             shall behave as if it were issued after the about-to-block thread
-             has blocked."
-           This is fulfilled here, because the thread signalling is done
-           through SetEvent, not PulseEvent.  */
-        /* Wait until another thread signals this event or until the abstime
-           passes.  */
-        gettimeofday (&currtime, NULL);
-        if (currtime.tv_sec > abstime->tv_sec)
-          timeout = 0;
-        else
-          {
-            unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
-            timeout = seconds * 1000;
-            if (timeout / 1000 != seconds) /* overflow? */
-              timeout = INFINITE;
-            else
-              {
-                long milliseconds =
-                  abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
-                if (milliseconds >= 0)
-                  {
-                    timeout += milliseconds;
-                    if (timeout < milliseconds) /* overflow? */
-                      timeout = INFINITE;
-                  }
-                else
-                  {
-                    if (timeout >= - milliseconds)
-                      timeout -= (- milliseconds);
-                    else
-                      timeout = 0;
-                  }
-              }
-          }
-        result = WaitForSingleObject (event, timeout);
-        if (result == WAIT_FAILED)
-          abort ();
-        if (result == WAIT_TIMEOUT)
-          {
-            EnterCriticalSection (&cond->lock);
-            if (gl_waitqueue_remove (&cond->waiters, elt))
-              {
-                /* The event was not signaled between the WaitForSingleObject
-                   call and the EnterCriticalSection call.  */
-                if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
-                  abort ();
-              }
-            else
-              {
-                /* The event was signaled between the WaitForSingleObject
-                   call and the EnterCriticalSection call.  */
-                if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
-                  abort ();
-                /* Produce the right return value.  */
-                result = WAIT_OBJECT_0;
-              }
-            LeaveCriticalSection (&cond->lock);
-          }
-        else
-          {
-            /* The thread which signalled the event already did the
-               bookkeeping: removed us from the waiters.  */
-          }
-        CloseHandle (event);
-        free (elt);
-        /* Take the lock again.  It does not matter whether this is done
-           before or after the bookkeeping for WAIT_TIMEOUT.  */
-        err = glthread_lock_lock (lock);
-        return (err ? err :
-                result == WAIT_OBJECT_0 ? 0 :
-                result == WAIT_TIMEOUT ? ETIMEDOUT :
-                /* WAIT_FAILED shouldn't happen */ EAGAIN);
-      }
+    gettimeofday (&currtime, NULL);
+    if (currtime.tv_sec > abstime->tv_sec
+        || (currtime.tv_sec == abstime->tv_sec
+            && currtime.tv_usec * 1000 >= abstime->tv_nsec))
+      return ETIMEDOUT;
+
+    EnterCriticalSection (&cond->lock);
+    {
+      struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
+      LeaveCriticalSection (&cond->lock);
+      if (elt == NULL)
+        {
+          /* Allocation failure.  Weird.  */
+          return EAGAIN;
+        }
+      else
+        {
+          HANDLE event = elt->event;
+          int err;
+          DWORD timeout;
+          DWORD result;
+
+          /* Now release the lock and let any other thread take it.  */
+          err = glthread_lock_unlock (lock);
+          if (err != 0)
+            {
+              EnterCriticalSection (&cond->lock);
+              gl_waitqueue_remove (&cond->waiters, elt);
+              LeaveCriticalSection (&cond->lock);
+              CloseHandle (event);
+              free (elt);
+              return err;
+            }
+          /* POSIX says:
+              "If another thread is able to acquire the mutex after the
+               about-to-block thread has released it, then a subsequent call to
+               pthread_cond_broadcast() or pthread_cond_signal() in that thread
+               shall behave as if it were issued after the about-to-block thread
+               has blocked."
+             This is fulfilled here, because the thread signalling is done
+             through SetEvent, not PulseEvent.  */
+          /* Wait until another thread signals this event or until the abstime
+             passes.  */
+          gettimeofday (&currtime, NULL);
+          if (currtime.tv_sec > abstime->tv_sec)
+            timeout = 0;
+          else
+            {
+              unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+              timeout = seconds * 1000;
+              if (timeout / 1000 != seconds) /* overflow? */
+                timeout = INFINITE;
+              else
+                {
+                  long milliseconds =
+                    abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
+                  if (milliseconds >= 0)
+                    {
+                      timeout += milliseconds;
+                      if (timeout < milliseconds) /* overflow? */
+                        timeout = INFINITE;
+                    }
+                  else
+                    {
+                      if (timeout >= - milliseconds)
+                        timeout -= (- milliseconds);
+                      else
+                        timeout = 0;
+                    }
+                }
+            }
+          result = WaitForSingleObject (event, timeout);
+          if (result == WAIT_FAILED)
+            abort ();
+          if (result == WAIT_TIMEOUT)
+            {
+              EnterCriticalSection (&cond->lock);
+              if (gl_waitqueue_remove (&cond->waiters, elt))
+                {
+                  /* The event was not signaled between the WaitForSingleObject
+                     call and the EnterCriticalSection call.  */
+                  if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
+                    abort ();
+                }
+              else
+                {
+                  /* The event was signaled between the WaitForSingleObject
+                     call and the EnterCriticalSection call.  */
+                  if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
+                    abort ();
+                  /* Produce the right return value.  */
+                  result = WAIT_OBJECT_0;
+                }
+              LeaveCriticalSection (&cond->lock);
+            }
+          else
+            {
+              /* The thread which signalled the event already did the
+                 bookkeeping: removed us from the waiters.  */
+            }
+          CloseHandle (event);
+          free (elt);
+          /* Take the lock again.  It does not matter whether this is done
+             before or after the bookkeeping for WAIT_TIMEOUT.  */
+          err = glthread_lock_lock (lock);
+          return (err ? err :
+                  result == WAIT_OBJECT_0 ? 0 :
+                  result == WAIT_TIMEOUT ? ETIMEDOUT :
+                  /* WAIT_FAILED shouldn't happen */ EAGAIN);
+        }
+    }
   }
 }
 
-- 
2.7.4


[-- Attachment #8: 0007-lock-cond-Avoid-possible-counter-wraparound-on-Windo.patch --]
[-- Type: text/x-patch, Size: 6925 bytes --]

From 7078fef393b8d06c522c690c1fe7017596b12734 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:04:08 +0200
Subject: [PATCH 07/26] lock, cond: Avoid possible counter wraparound on
 Windows.

* lib/glthread/lock.c (glthread_lock_lock_func): Leave the 'started'
field of the guard unchanged if it was already positive.
(glthread_rwlock_rdlock_func): Likewise.
(glthread_rwlock_wrlock_func): Likewise.
(glthread_recursive_lock_lock_func): Likewise.
* lib/glthread/cond.c (glthread_cond_wait_func): Likewise.
(glthread_cond_timedwait_func): Likewise.
---
 ChangeLog           | 11 +++++++++++
 lib/glthread/cond.c | 24 ++++++++++++++++--------
 lib/glthread/lock.c | 50 +++++++++++++++++++++++++++++++++-----------------
 3 files changed, 60 insertions(+), 25 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5271997..ba91fda 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	lock, cond: Avoid possible counter wraparound on Windows.
+	* lib/glthread/lock.c (glthread_lock_lock_func): Leave the 'started'
+	field of the guard unchanged if it was already positive.
+	(glthread_rwlock_rdlock_func): Likewise.
+	(glthread_rwlock_wrlock_func): Likewise.
+	(glthread_recursive_lock_lock_func): Likewise.
+	* lib/glthread/cond.c (glthread_cond_wait_func): Likewise.
+	(glthread_cond_timedwait_func): Likewise.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	cond: Make glthread_cond_timedwait more reliable on Windows.
 	* lib/glthread/cond.c (glthread_cond_timedwait_func): Initialize the
 	condition variable before looking at the current time.
diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c
index b0304bc..6df6780 100644
--- a/lib/glthread/cond.c
+++ b/lib/glthread/cond.c
@@ -229,10 +229,14 @@ glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
            Initialize it.  */
         glthread_cond_init (cond);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this condition variable.  */
-        while (!cond->guard.done)
-          Sleep (0);
+        {
+          /* Don't let cond->guard.started grow and wrap around.  */
+          InterlockedDecrement (&cond->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this condition variable.  */
+          while (!cond->guard.done)
+            Sleep (0);
+        }
     }
 
   EnterCriticalSection (&cond->lock);
@@ -292,10 +296,14 @@ glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec
            Initialize it.  */
         glthread_cond_init (cond);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this condition variable.  */
-        while (!cond->guard.done)
-          Sleep (0);
+        {
+          /* Don't let cond->guard.started grow and wrap around.  */
+          InterlockedDecrement (&cond->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this condition variable.  */
+          while (!cond->guard.done)
+            Sleep (0);
+        }
     }
 
   {
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index 2af4f26..4774573 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -811,10 +811,14 @@ glthread_lock_lock_func (gl_lock_t *lock)
         /* This thread is the first one to need this lock.  Initialize it.  */
         glthread_lock_init (lock);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this lock.  */
-        while (!lock->guard.done)
-          Sleep (0);
+        {
+          /* Don't let lock->guard.started grow and wrap around.  */
+          InterlockedDecrement (&lock->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this lock.  */
+          while (!lock->guard.done)
+            Sleep (0);
+        }
     }
   EnterCriticalSection (&lock->lock);
   return 0;
@@ -951,10 +955,14 @@ glthread_rwlock_rdlock_func (gl_rwlock_t *lock)
         /* This thread is the first one to need this lock.  Initialize it.  */
         glthread_rwlock_init (lock);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this lock.  */
-        while (!lock->guard.done)
-          Sleep (0);
+        {
+          /* Don't let lock->guard.started grow and wrap around.  */
+          InterlockedDecrement (&lock->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this lock.  */
+          while (!lock->guard.done)
+            Sleep (0);
+        }
     }
   EnterCriticalSection (&lock->lock);
   /* Test whether only readers are currently running, and whether the runcount
@@ -1007,10 +1015,14 @@ glthread_rwlock_wrlock_func (gl_rwlock_t *lock)
         /* This thread is the first one to need this lock.  Initialize it.  */
         glthread_rwlock_init (lock);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this lock.  */
-        while (!lock->guard.done)
-          Sleep (0);
+        {
+          /* Don't let lock->guard.started grow and wrap around.  */
+          InterlockedDecrement (&lock->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this lock.  */
+          while (!lock->guard.done)
+            Sleep (0);
+        }
     }
   EnterCriticalSection (&lock->lock);
   /* Test whether no readers or writers are currently running.  */
@@ -1131,10 +1143,14 @@ glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
         /* This thread is the first one to need this lock.  Initialize it.  */
         glthread_recursive_lock_init (lock);
       else
-        /* Yield the CPU while waiting for another thread to finish
-           initializing this lock.  */
-        while (!lock->guard.done)
-          Sleep (0);
+        {
+          /* Don't let lock->guard.started grow and wrap around.  */
+          InterlockedDecrement (&lock->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this lock.  */
+          while (!lock->guard.done)
+            Sleep (0);
+        }
     }
   {
     DWORD self = GetCurrentThreadId ();
@@ -1196,7 +1212,7 @@ glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
         }
       else
         {
-          /* Undo last operation.  */
+          /* Don't let once_control->started grow and wrap around.  */
           InterlockedDecrement (&once_control->started);
           /* Some other thread has already started the initialization.
              Yield the CPU while waiting for the other thread to finish
-- 
2.7.4


[-- Attachment #9: 0008-windows-once-New-module.patch --]
[-- Type: text/x-patch, Size: 10066 bytes --]

From 6c3d118e8303b1f2cfdf17749d39392f1340077d Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:04:45 +0200
Subject: [PATCH 08/26] windows-once: New module.

* lib/windows-once.h: New file, extracted from lib/glthread/lock.h.
* lib/windows-once.c: New file, extracted from lib/glthread/lock.c.
* lib/glthread/lock.h: Include windows-once.h.
(gl_once_t): Define using glwthread_once_t.
(gl_once_define): Define using GLWTHREAD_ONCE_INIT.
(glthread_once): Define using glwthread_once.
(glthread_once_func): Remove declaration.
* lib/glthread/lock.c (glthread_once_func): Remove function.
* modules/windows-once: New file.
* modules/lock (Depends-on): Add windows-once.
---
 ChangeLog            | 14 ++++++++++++
 lib/glthread/lock.c  | 39 ---------------------------------
 lib/glthread/lock.h  | 15 +++++--------
 lib/windows-once.c   | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-once.h   | 47 +++++++++++++++++++++++++++++++++++++++
 modules/lock         |  1 +
 modules/windows-once | 27 +++++++++++++++++++++++
 7 files changed, 156 insertions(+), 49 deletions(-)
 create mode 100644 lib/windows-once.c
 create mode 100644 lib/windows-once.h
 create mode 100644 modules/windows-once

diff --git a/ChangeLog b/ChangeLog
index ba91fda..06474ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-once: New module.
+	* lib/windows-once.h: New file, extracted from lib/glthread/lock.h.
+	* lib/windows-once.c: New file, extracted from lib/glthread/lock.c.
+	* lib/glthread/lock.h: Include windows-once.h.
+	(gl_once_t): Define using glwthread_once_t.
+	(gl_once_define): Define using GLWTHREAD_ONCE_INIT.
+	(glthread_once): Define using glwthread_once.
+	(glthread_once_func): Remove declaration.
+	* lib/glthread/lock.c (glthread_once_func): Remove function.
+	* modules/windows-once: New file.
+	* modules/lock (Depends-on): Add windows-once.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	lock, cond: Avoid possible counter wraparound on Windows.
 	* lib/glthread/lock.c (glthread_lock_lock_func): Leave the 'started'
 	field of the guard unchanged if it was already positive.
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index 4774573..587d1e7 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -1193,45 +1193,6 @@ glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
   return 0;
 }
 
-/* -------------------------- gl_once_t datatype -------------------------- */
-
-void
-glthread_once_func (gl_once_t *once_control, void (*initfunction) (void))
-{
-  if (once_control->inited <= 0)
-    {
-      if (InterlockedIncrement (&once_control->started) == 0)
-        {
-          /* This thread is the first one to come to this once_control.  */
-          InitializeCriticalSection (&once_control->lock);
-          EnterCriticalSection (&once_control->lock);
-          once_control->inited = 0;
-          initfunction ();
-          once_control->inited = 1;
-          LeaveCriticalSection (&once_control->lock);
-        }
-      else
-        {
-          /* Don't let once_control->started grow and wrap around.  */
-          InterlockedDecrement (&once_control->started);
-          /* Some other thread has already started the initialization.
-             Yield the CPU while waiting for the other thread to finish
-             initializing and taking the lock.  */
-          while (once_control->inited < 0)
-            Sleep (0);
-          if (once_control->inited <= 0)
-            {
-              /* Take the lock.  This blocks until the other thread has
-                 finished calling the initfunction.  */
-              EnterCriticalSection (&once_control->lock);
-              LeaveCriticalSection (&once_control->lock);
-              if (!(once_control->inited > 0))
-                abort ();
-            }
-        }
-    }
-}
-
 #endif
 
 /* ========================================================================= */
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index cc6fb5e..93e2e48 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -690,6 +690,8 @@ extern int glthread_once_singlethreaded (gl_once_t *once_control);
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 # include <windows.h>
 
+# include "windows-once.h"
+
 # ifdef __cplusplus
 extern "C" {
 # endif
@@ -814,18 +816,11 @@ extern int glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock);
 
 /* -------------------------- gl_once_t datatype -------------------------- */
 
-typedef struct
-        {
-          volatile int inited;
-          volatile long started;
-          CRITICAL_SECTION lock;
-        }
-        gl_once_t;
+typedef glwthread_once_t gl_once_t;
 # define gl_once_define(STORAGECLASS, NAME) \
-    STORAGECLASS gl_once_t NAME = { -1, -1 };
+    STORAGECLASS gl_once_t NAME = GLWTHREAD_ONCE_INIT;
 # define glthread_once(ONCE_CONTROL, INITFUNCTION) \
-    (glthread_once_func (ONCE_CONTROL, INITFUNCTION), 0)
-extern void glthread_once_func (gl_once_t *once_control, void (*initfunction) (void));
+    (glwthread_once (ONCE_CONTROL, INITFUNCTION), 0)
 
 # ifdef __cplusplus
 }
diff --git a/lib/windows-once.c b/lib/windows-once.c
new file mode 100644
index 0000000..4a18922
--- /dev/null
+++ b/lib/windows-once.c
@@ -0,0 +1,62 @@
+/* Once-only control (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-once.h"
+
+#include <stdlib.h>
+
+void
+glwthread_once (glwthread_once_t *once_control, void (*initfunction) (void))
+{
+  if (once_control->inited <= 0)
+    {
+      if (InterlockedIncrement (&once_control->started) == 0)
+        {
+          /* This thread is the first one to come to this once_control.  */
+          InitializeCriticalSection (&once_control->lock);
+          EnterCriticalSection (&once_control->lock);
+          once_control->inited = 0;
+          initfunction ();
+          once_control->inited = 1;
+          LeaveCriticalSection (&once_control->lock);
+        }
+      else
+        {
+          /* Don't let once_control->started grow and wrap around.  */
+          InterlockedDecrement (&once_control->started);
+          /* Some other thread has already started the initialization.
+             Yield the CPU while waiting for the other thread to finish
+             initializing and taking the lock.  */
+          while (once_control->inited < 0)
+            Sleep (0);
+          if (once_control->inited <= 0)
+            {
+              /* Take the lock.  This blocks until the other thread has
+                 finished calling the initfunction.  */
+              EnterCriticalSection (&once_control->lock);
+              LeaveCriticalSection (&once_control->lock);
+              if (!(once_control->inited > 0))
+                abort ();
+            }
+        }
+    }
+}
diff --git a/lib/windows-once.h b/lib/windows-once.h
new file mode 100644
index 0000000..8b56a39
--- /dev/null
+++ b/lib/windows-once.h
@@ -0,0 +1,47 @@
+/* Once-only control (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_ONCE_H
+#define _WINDOWS_ONCE_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+typedef struct
+        {
+          volatile int inited;
+          volatile LONG started;
+          CRITICAL_SECTION lock;
+        }
+        glwthread_once_t;
+
+#define GLWTHREAD_ONCE_INIT { -1, -1 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void glwthread_once (glwthread_once_t *once_control,
+                            void (*initfunction) (void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_ONCE_H */
diff --git a/modules/lock b/modules/lock
index dafeeeb..fca8341 100644
--- a/modules/lock
+++ b/modules/lock
@@ -10,6 +10,7 @@ m4/pthread_rwlock_rdlock.m4
 Depends-on:
 extensions
 threadlib
+windows-once    [test $gl_threads_api = windows]
 
 configure.ac:
 gl_LOCK
diff --git a/modules/windows-once b/modules/windows-once
new file mode 100644
index 0000000..34d5b1e
--- /dev/null
+++ b/modules/windows-once
@@ -0,0 +1,27 @@
+Description:
+Once-only control (native Windows implementation).
+
+Files:
+lib/windows-once.h
+lib/windows-once.c
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-once])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-once.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #10: 0009-windows-mutex-New-module.patch --]
[-- Type: text/x-patch, Size: 16441 bytes --]

From 2535ce3ac9fdd46ec44b0fa5f1e4f6dcaaaebd99 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:08:16 +0200
Subject: [PATCH 09/26] windows-mutex: New module.

* lib/windows-mutex.h: New file, extracted from lib/glthread/lock.h.
* lib/windows-mutex.c: New file, extracted from lib/glthread/lock.c.
* lib/windows-spinlock.h: New file, extracted from lib/glthread/lock.h.
* lib/glthread/lock.h: Include windows-spinlock.h, windows-mutex.h.
(gl_spinlock_t): Remove type.
(gl_lock_t): Define using glwthread_mutex_t.
(gl_lock_initializer): Define using GLWTHREAD_MUTEX_INIT.
(glthread_lock_init): Define using glwthread_mutex_init.
(glthread_lock_lock): Define using glwthread_mutex_lock.
(glthread_lock_unlock): Define using glwthread_mutex_unlock.
(glthread_lock_destroy): Define using glwthread_mutex_destroy.
(glthread_lock_init_func, glthread_lock_lock_func,
glthread_lock_unlock_func, glthread_lock_destroy_func): Remove
declarations.
Use glwthread_spinlock_t instead of gl_spinlock_t.
(gl_rwlock_initializer, gl_recursive_lock_initializer): Define using
GLWTHREAD_SPINLOCK_INIT.
* lib/glthread/lock.c (glthread_lock_init_func, glthread_lock_lock_func,
glthread_lock_unlock_func, glthread_lock_destroy_func): Remove
functions.
* lib/glthread/cond.h: Use glwthread_spinlock_t instead of
gl_spinlock_t.
* modules/windows-mutex: New file.
* modules/lock (Depends-on): Add windows-mutex.
---
 ChangeLog              | 28 +++++++++++++++
 lib/glthread/cond.h    |  2 +-
 lib/glthread/lock.c    | 50 --------------------------
 lib/glthread/lock.h    | 33 +++++++-----------
 lib/windows-mutex.c    | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-mutex.h    | 51 +++++++++++++++++++++++++++
 lib/windows-spinlock.h | 35 +++++++++++++++++++
 modules/lock           |  1 +
 modules/windows-mutex  | 28 +++++++++++++++
 9 files changed, 251 insertions(+), 72 deletions(-)
 create mode 100644 lib/windows-mutex.c
 create mode 100644 lib/windows-mutex.h
 create mode 100644 lib/windows-spinlock.h
 create mode 100644 modules/windows-mutex

diff --git a/ChangeLog b/ChangeLog
index 06474ea..a168b23 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,33 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-mutex: New module.
+	* lib/windows-mutex.h: New file, extracted from lib/glthread/lock.h.
+	* lib/windows-mutex.c: New file, extracted from lib/glthread/lock.c.
+	* lib/windows-spinlock.h: New file, extracted from lib/glthread/lock.h.
+	* lib/glthread/lock.h: Include windows-spinlock.h, windows-mutex.h.
+	(gl_spinlock_t): Remove type.
+	(gl_lock_t): Define using glwthread_mutex_t.
+	(gl_lock_initializer): Define using GLWTHREAD_MUTEX_INIT.
+	(glthread_lock_init): Define using glwthread_mutex_init.
+	(glthread_lock_lock): Define using glwthread_mutex_lock.
+	(glthread_lock_unlock): Define using glwthread_mutex_unlock.
+	(glthread_lock_destroy): Define using glwthread_mutex_destroy.
+	(glthread_lock_init_func, glthread_lock_lock_func,
+	glthread_lock_unlock_func, glthread_lock_destroy_func): Remove
+	declarations.
+	Use glwthread_spinlock_t instead of gl_spinlock_t.
+	(gl_rwlock_initializer, gl_recursive_lock_initializer): Define using
+	GLWTHREAD_SPINLOCK_INIT.
+	* lib/glthread/lock.c (glthread_lock_init_func, glthread_lock_lock_func,
+	glthread_lock_unlock_func, glthread_lock_destroy_func): Remove
+	functions.
+	* lib/glthread/cond.h: Use glwthread_spinlock_t instead of
+	gl_spinlock_t.
+	* modules/windows-mutex: New file.
+	* modules/lock (Depends-on): Add windows-mutex.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-once: New module.
 	* lib/windows-once.h: New file, extracted from lib/glthread/lock.h.
 	* lib/windows-once.c: New file, extracted from lib/glthread/lock.c.
diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h
index 6540f1d..f252c32 100644
--- a/lib/glthread/cond.h
+++ b/lib/glthread/cond.h
@@ -311,7 +311,7 @@ typedef struct
         gl_linked_waitqueue_t;
 typedef struct
         {
-          gl_spinlock_t guard; /* protects the initialization */
+          glwthread_spinlock_t guard; /* protects the initialization */
           CRITICAL_SECTION lock; /* protects the remaining fields */
           gl_linked_waitqueue_t waiters; /* waiting threads */
         }
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index 587d1e7..e17f524 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -793,56 +793,6 @@ glthread_once_singlethreaded (gl_once_t *once_control)
 
 #if USE_WINDOWS_THREADS
 
-/* -------------------------- gl_lock_t datatype -------------------------- */
-
-void
-glthread_lock_init_func (gl_lock_t *lock)
-{
-  InitializeCriticalSection (&lock->lock);
-  lock->guard.done = 1;
-}
-
-int
-glthread_lock_lock_func (gl_lock_t *lock)
-{
-  if (!lock->guard.done)
-    {
-      if (InterlockedIncrement (&lock->guard.started) == 0)
-        /* This thread is the first one to need this lock.  Initialize it.  */
-        glthread_lock_init (lock);
-      else
-        {
-          /* Don't let lock->guard.started grow and wrap around.  */
-          InterlockedDecrement (&lock->guard.started);
-          /* Yield the CPU while waiting for another thread to finish
-             initializing this lock.  */
-          while (!lock->guard.done)
-            Sleep (0);
-        }
-    }
-  EnterCriticalSection (&lock->lock);
-  return 0;
-}
-
-int
-glthread_lock_unlock_func (gl_lock_t *lock)
-{
-  if (!lock->guard.done)
-    return EINVAL;
-  LeaveCriticalSection (&lock->lock);
-  return 0;
-}
-
-int
-glthread_lock_destroy_func (gl_lock_t *lock)
-{
-  if (!lock->guard.done)
-    return EINVAL;
-  DeleteCriticalSection (&lock->lock);
-  lock->guard.done = 0;
-  return 0;
-}
-
 /* ------------------------- gl_rwlock_t datatype ------------------------- */
 
 /* In this file, the waitqueues are implemented as circular arrays.  */
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 93e2e48..e2a49dc 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -690,6 +690,8 @@ extern int glthread_once_singlethreaded (gl_once_t *once_control);
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 # include <windows.h>
 
+# include "windows-spinlock.h"
+# include "windows-mutex.h"
 # include "windows-once.h"
 
 # ifdef __cplusplus
@@ -707,34 +709,23 @@ extern "C" {
 /* There is no way to statically initialize a CRITICAL_SECTION.  It needs
    to be done lazily, once only.  For this we need spinlocks.  */
 
-typedef struct { volatile int done; volatile long started; } gl_spinlock_t;
-
 /* -------------------------- gl_lock_t datatype -------------------------- */
 
-typedef struct
-        {
-          gl_spinlock_t guard; /* protects the initialization */
-          CRITICAL_SECTION lock;
-        }
-        gl_lock_t;
+typedef glwthread_mutex_t gl_lock_t;
 # define gl_lock_define(STORAGECLASS, NAME) \
     STORAGECLASS gl_lock_t NAME;
 # define gl_lock_define_initialized(STORAGECLASS, NAME) \
     STORAGECLASS gl_lock_t NAME = gl_lock_initializer;
 # define gl_lock_initializer \
-    { { 0, -1 } }
+    GLWTHREAD_MUTEX_INIT
 # define glthread_lock_init(LOCK) \
-    (glthread_lock_init_func (LOCK), 0)
+    (glwthread_mutex_init (LOCK), 0)
 # define glthread_lock_lock(LOCK) \
-    glthread_lock_lock_func (LOCK)
+    glwthread_mutex_lock (LOCK)
 # define glthread_lock_unlock(LOCK) \
-    glthread_lock_unlock_func (LOCK)
+    glwthread_mutex_unlock (LOCK)
 # define glthread_lock_destroy(LOCK) \
-    glthread_lock_destroy_func (LOCK)
-extern void glthread_lock_init_func (gl_lock_t *lock);
-extern int glthread_lock_lock_func (gl_lock_t *lock);
-extern int glthread_lock_unlock_func (gl_lock_t *lock);
-extern int glthread_lock_destroy_func (gl_lock_t *lock);
+    glwthread_mutex_destroy (LOCK)
 
 /* ------------------------- gl_rwlock_t datatype ------------------------- */
 
@@ -752,7 +743,7 @@ typedef struct
         gl_carray_waitqueue_t;
 typedef struct
         {
-          gl_spinlock_t guard; /* protects the initialization */
+          glwthread_spinlock_t guard; /* protects the initialization */
           CRITICAL_SECTION lock; /* protects the remaining fields */
           gl_carray_waitqueue_t waiting_readers; /* waiting readers */
           gl_carray_waitqueue_t waiting_writers; /* waiting writers */
@@ -764,7 +755,7 @@ typedef struct
 # define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
     STORAGECLASS gl_rwlock_t NAME = gl_rwlock_initializer;
 # define gl_rwlock_initializer \
-    { { 0, -1 } }
+    { GLWTHREAD_SPINLOCK_INIT }
 # define glthread_rwlock_init(LOCK) \
     (glthread_rwlock_init_func (LOCK), 0)
 # define glthread_rwlock_rdlock(LOCK) \
@@ -789,7 +780,7 @@ extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock);
 
 typedef struct
         {
-          gl_spinlock_t guard; /* protects the initialization */
+          glwthread_spinlock_t guard; /* protects the initialization */
           DWORD owner;
           unsigned long depth;
           CRITICAL_SECTION lock;
@@ -800,7 +791,7 @@ typedef struct
 # define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
     STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer;
 # define gl_recursive_lock_initializer \
-    { { 0, -1 }, 0, 0 }
+    { GLWTHREAD_SPINLOCK_INIT, 0, 0 }
 # define glthread_recursive_lock_init(LOCK) \
     (glthread_recursive_lock_init_func (LOCK), 0)
 # define glthread_recursive_lock_lock(LOCK) \
diff --git a/lib/windows-mutex.c b/lib/windows-mutex.c
new file mode 100644
index 0000000..e2c94b8
--- /dev/null
+++ b/lib/windows-mutex.c
@@ -0,0 +1,95 @@
+/* Plain mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-mutex.h"
+
+#include <errno.h>
+
+void
+glwthread_mutex_init (glwthread_mutex_t *mutex)
+{
+  InitializeCriticalSection (&mutex->lock);
+  mutex->guard.done = 1;
+}
+
+int
+glwthread_mutex_lock (glwthread_mutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        /* This thread is the first one to need this mutex.  Initialize it.  */
+        glwthread_mutex_init (mutex);
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+  EnterCriticalSection (&mutex->lock);
+  return 0;
+}
+
+int
+glwthread_mutex_trylock (glwthread_mutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        /* This thread is the first one to need this mutex.  Initialize it.  */
+        glwthread_mutex_init (mutex);
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Let another thread finish initializing this mutex, and let it also
+             lock this mutex.  */
+          return EBUSY;
+        }
+    }
+  if (!TryEnterCriticalSection (&mutex->lock))
+    return EBUSY;
+  return 0;
+}
+
+int
+glwthread_mutex_unlock (glwthread_mutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    return EINVAL;
+  LeaveCriticalSection (&mutex->lock);
+  return 0;
+}
+
+int
+glwthread_mutex_destroy (glwthread_mutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    return EINVAL;
+  DeleteCriticalSection (&mutex->lock);
+  mutex->guard.done = 0;
+  return 0;
+}
diff --git a/lib/windows-mutex.h b/lib/windows-mutex.h
new file mode 100644
index 0000000..edc738e
--- /dev/null
+++ b/lib/windows-mutex.h
@@ -0,0 +1,51 @@
+/* Plain mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_MUTEX_H
+#define _WINDOWS_MUTEX_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+#include "windows-spinlock.h"
+
+typedef struct
+        {
+          glwthread_spinlock_t guard; /* protects the initialization */
+          CRITICAL_SECTION lock;
+        }
+        glwthread_mutex_t;
+
+#define GLWTHREAD_MUTEX_INIT { GLWTHREAD_SPINLOCK_INIT }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void glwthread_mutex_init (glwthread_mutex_t *mutex);
+extern int glwthread_mutex_lock (glwthread_mutex_t *mutex);
+extern int glwthread_mutex_trylock (glwthread_mutex_t *mutex);
+extern int glwthread_mutex_unlock (glwthread_mutex_t *mutex);
+extern int glwthread_mutex_destroy (glwthread_mutex_t *mutex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_MUTEX_H */
diff --git a/lib/windows-spinlock.h b/lib/windows-spinlock.h
new file mode 100644
index 0000000..26a4b65
--- /dev/null
+++ b/lib/windows-spinlock.h
@@ -0,0 +1,35 @@
+/* Spinlocks (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_SPINLOCK_H
+#define _WINDOWS_SPINLOCK_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+typedef struct
+        {
+          volatile int done;
+          volatile LONG started;
+        }
+        glwthread_spinlock_t;
+
+#define GLWTHREAD_SPINLOCK_INIT { 0, -1 }
+
+#endif /* _WINDOWS_SPINLOCK_H */
diff --git a/modules/lock b/modules/lock
index fca8341..351e902 100644
--- a/modules/lock
+++ b/modules/lock
@@ -10,6 +10,7 @@ m4/pthread_rwlock_rdlock.m4
 Depends-on:
 extensions
 threadlib
+windows-mutex   [test $gl_threads_api = windows]
 windows-once    [test $gl_threads_api = windows]
 
 configure.ac:
diff --git a/modules/windows-mutex b/modules/windows-mutex
new file mode 100644
index 0000000..18aabcc
--- /dev/null
+++ b/modules/windows-mutex
@@ -0,0 +1,28 @@
+Description:
+Plain mutexes (native Windows implementation).
+
+Files:
+lib/windows-mutex.h
+lib/windows-mutex.c
+lib/windows-spinlock.h
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-mutex])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-mutex.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #11: 0010-windows-recmutex-New-module.patch --]
[-- Type: text/x-patch, Size: 14559 bytes --]

From 4570a616244efbc2fa4bd8788f27ef0e4b48bdbe Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:10:39 +0200
Subject: [PATCH 10/26] windows-recmutex: New module.

* lib/windows-recmutex.h: New file, extracted from lib/glthread/lock.h.
* lib/windows-recmutex.c: New file, extracted from lib/glthread/lock.c.
* lib/glthread/lock.h: Include windows-recmutex.h.
(gl_recursive_lock_t): Define using glwthread_recmutex_t.
(gl_recursive_lock_initializer): Define using GLWTHREAD_RECMUTEX_INIT.
(glthread_recursive_lock_init): Define using glwthread_recmutex_init.
(glthread_recursive_lock_lock): Define using glwthread_recmutex_lock.
(glthread_recursive_lock_unlock): Define using
glwthread_recmutex_unlock.
(glthread_recursive_lock_destroy): Define using
glwthread_recmutex_destroy.
(glthread_recursive_lock_init_func, glthread_recursive_lock_lock_func,
glthread_recursive_lock_unlock_func,
glthread_recursive_lock_destroy_func): Remove declarations.
* lib/glthread/lock.c (glthread_recursive_lock_init_func,
glthread_recursive_lock_lock_func, glthread_recursive_lock_unlock_func,
glthread_recursive_lock_destroy_func): Remove functions.
* modules/windows-recmutex: New file.
* modules/lock (Depends-on): Add windows-recmutex.
---
 ChangeLog                |  23 +++++++++
 lib/glthread/lock.c      |  70 --------------------------
 lib/glthread/lock.h      |  28 +++--------
 lib/windows-recmutex.c   | 127 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-recmutex.h   |  57 +++++++++++++++++++++
 modules/lock             |   5 +-
 modules/windows-recmutex |  28 +++++++++++
 7 files changed, 245 insertions(+), 93 deletions(-)
 create mode 100644 lib/windows-recmutex.c
 create mode 100644 lib/windows-recmutex.h
 create mode 100644 modules/windows-recmutex

diff --git a/ChangeLog b/ChangeLog
index a168b23..1ac5490 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,28 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-recmutex: New module.
+	* lib/windows-recmutex.h: New file, extracted from lib/glthread/lock.h.
+	* lib/windows-recmutex.c: New file, extracted from lib/glthread/lock.c.
+	* lib/glthread/lock.h: Include windows-recmutex.h.
+	(gl_recursive_lock_t): Define using glwthread_recmutex_t.
+	(gl_recursive_lock_initializer): Define using GLWTHREAD_RECMUTEX_INIT.
+	(glthread_recursive_lock_init): Define using glwthread_recmutex_init.
+	(glthread_recursive_lock_lock): Define using glwthread_recmutex_lock.
+	(glthread_recursive_lock_unlock): Define using
+	glwthread_recmutex_unlock.
+	(glthread_recursive_lock_destroy): Define using
+	glwthread_recmutex_destroy.
+	(glthread_recursive_lock_init_func, glthread_recursive_lock_lock_func,
+	glthread_recursive_lock_unlock_func,
+	glthread_recursive_lock_destroy_func): Remove declarations.
+	* lib/glthread/lock.c (glthread_recursive_lock_init_func,
+	glthread_recursive_lock_lock_func, glthread_recursive_lock_unlock_func,
+	glthread_recursive_lock_destroy_func): Remove functions.
+	* modules/windows-recmutex: New file.
+	* modules/lock (Depends-on): Add windows-recmutex.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-mutex: New module.
 	* lib/windows-mutex.h: New file, extracted from lib/glthread/lock.h.
 	* lib/windows-mutex.c: New file, extracted from lib/glthread/lock.c.
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index e17f524..a686a30 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -1073,76 +1073,6 @@ glthread_rwlock_destroy_func (gl_rwlock_t *lock)
   return 0;
 }
 
-/* --------------------- gl_recursive_lock_t datatype --------------------- */
-
-void
-glthread_recursive_lock_init_func (gl_recursive_lock_t *lock)
-{
-  lock->owner = 0;
-  lock->depth = 0;
-  InitializeCriticalSection (&lock->lock);
-  lock->guard.done = 1;
-}
-
-int
-glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock)
-{
-  if (!lock->guard.done)
-    {
-      if (InterlockedIncrement (&lock->guard.started) == 0)
-        /* This thread is the first one to need this lock.  Initialize it.  */
-        glthread_recursive_lock_init (lock);
-      else
-        {
-          /* Don't let lock->guard.started grow and wrap around.  */
-          InterlockedDecrement (&lock->guard.started);
-          /* Yield the CPU while waiting for another thread to finish
-             initializing this lock.  */
-          while (!lock->guard.done)
-            Sleep (0);
-        }
-    }
-  {
-    DWORD self = GetCurrentThreadId ();
-    if (lock->owner != self)
-      {
-        EnterCriticalSection (&lock->lock);
-        lock->owner = self;
-      }
-    if (++(lock->depth) == 0) /* wraparound? */
-      {
-        lock->depth--;
-        return EAGAIN;
-      }
-  }
-  return 0;
-}
-
-int
-glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock)
-{
-  if (lock->owner != GetCurrentThreadId ())
-    return EPERM;
-  if (lock->depth == 0)
-    return EINVAL;
-  if (--(lock->depth) == 0)
-    {
-      lock->owner = 0;
-      LeaveCriticalSection (&lock->lock);
-    }
-  return 0;
-}
-
-int
-glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock)
-{
-  if (lock->owner != 0)
-    return EBUSY;
-  DeleteCriticalSection (&lock->lock);
-  lock->guard.done = 0;
-  return 0;
-}
-
 #endif
 
 /* ========================================================================= */
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index e2a49dc..fb1ebc6 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -692,6 +692,7 @@ extern int glthread_once_singlethreaded (gl_once_t *once_control);
 
 # include "windows-spinlock.h"
 # include "windows-mutex.h"
+# include "windows-recmutex.h"
 # include "windows-once.h"
 
 # ifdef __cplusplus
@@ -774,36 +775,21 @@ extern int glthread_rwlock_destroy_func (gl_rwlock_t *lock);
 
 /* --------------------- gl_recursive_lock_t datatype --------------------- */
 
-/* The native Windows documentation says that CRITICAL_SECTION already
-   implements a recursive lock.  But we need not rely on it: It's easy to
-   implement a recursive lock without this assumption.  */
-
-typedef struct
-        {
-          glwthread_spinlock_t guard; /* protects the initialization */
-          DWORD owner;
-          unsigned long depth;
-          CRITICAL_SECTION lock;
-        }
-        gl_recursive_lock_t;
+typedef glwthread_recmutex_t gl_recursive_lock_t;
 # define gl_recursive_lock_define(STORAGECLASS, NAME) \
     STORAGECLASS gl_recursive_lock_t NAME;
 # define gl_recursive_lock_define_initialized(STORAGECLASS, NAME) \
     STORAGECLASS gl_recursive_lock_t NAME = gl_recursive_lock_initializer;
 # define gl_recursive_lock_initializer \
-    { GLWTHREAD_SPINLOCK_INIT, 0, 0 }
+    GLWTHREAD_RECMUTEX_INIT
 # define glthread_recursive_lock_init(LOCK) \
-    (glthread_recursive_lock_init_func (LOCK), 0)
+    (glwthread_recmutex_init (LOCK), 0)
 # define glthread_recursive_lock_lock(LOCK) \
-    glthread_recursive_lock_lock_func (LOCK)
+    glwthread_recmutex_lock (LOCK)
 # define glthread_recursive_lock_unlock(LOCK) \
-    glthread_recursive_lock_unlock_func (LOCK)
+    glwthread_recmutex_unlock (LOCK)
 # define glthread_recursive_lock_destroy(LOCK) \
-    glthread_recursive_lock_destroy_func (LOCK)
-extern void glthread_recursive_lock_init_func (gl_recursive_lock_t *lock);
-extern int glthread_recursive_lock_lock_func (gl_recursive_lock_t *lock);
-extern int glthread_recursive_lock_unlock_func (gl_recursive_lock_t *lock);
-extern int glthread_recursive_lock_destroy_func (gl_recursive_lock_t *lock);
+    glwthread_recmutex_destroy (LOCK)
 
 /* -------------------------- gl_once_t datatype -------------------------- */
 
diff --git a/lib/windows-recmutex.c b/lib/windows-recmutex.c
new file mode 100644
index 0000000..9629bf4
--- /dev/null
+++ b/lib/windows-recmutex.c
@@ -0,0 +1,127 @@
+/* Plain recursive mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-recmutex.h"
+
+#include <errno.h>
+
+void
+glwthread_recmutex_init (glwthread_recmutex_t *mutex)
+{
+  mutex->owner = 0;
+  mutex->depth = 0;
+  InitializeCriticalSection (&mutex->lock);
+  mutex->guard.done = 1;
+}
+
+int
+glwthread_recmutex_lock (glwthread_recmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        /* This thread is the first one to need this mutex.  Initialize it.  */
+        glwthread_recmutex_init (mutex);
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+  {
+    DWORD self = GetCurrentThreadId ();
+    if (mutex->owner != self)
+      {
+        EnterCriticalSection (&mutex->lock);
+        mutex->owner = self;
+      }
+    if (++(mutex->depth) == 0) /* wraparound? */
+      {
+        mutex->depth--;
+        return EAGAIN;
+      }
+  }
+  return 0;
+}
+
+int
+glwthread_recmutex_trylock (glwthread_recmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        /* This thread is the first one to need this mutex.  Initialize it.  */
+        glwthread_recmutex_init (mutex);
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Let another thread finish initializing this mutex, and let it also
+             lock this mutex.  */
+          return EBUSY;
+        }
+    }
+  {
+    DWORD self = GetCurrentThreadId ();
+    if (mutex->owner != self)
+      {
+        if (!TryEnterCriticalSection (&mutex->lock))
+          return EBUSY;
+        mutex->owner = self;
+      }
+    if (++(mutex->depth) == 0) /* wraparound? */
+      {
+        mutex->depth--;
+        return EAGAIN;
+      }
+  }
+  return 0;
+}
+
+int
+glwthread_recmutex_unlock (glwthread_recmutex_t *mutex)
+{
+  if (mutex->owner != GetCurrentThreadId ())
+    return EPERM;
+  if (mutex->depth == 0)
+    return EINVAL;
+  if (--(mutex->depth) == 0)
+    {
+      mutex->owner = 0;
+      LeaveCriticalSection (&mutex->lock);
+    }
+  return 0;
+}
+
+int
+glwthread_recmutex_destroy (glwthread_recmutex_t *mutex)
+{
+  if (mutex->owner != 0)
+    return EBUSY;
+  DeleteCriticalSection (&mutex->lock);
+  mutex->guard.done = 0;
+  return 0;
+}
diff --git a/lib/windows-recmutex.h b/lib/windows-recmutex.h
new file mode 100644
index 0000000..a4fcb47
--- /dev/null
+++ b/lib/windows-recmutex.h
@@ -0,0 +1,57 @@
+/* Plain recursive mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_RECMUTEX_H
+#define _WINDOWS_RECMUTEX_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+#include "windows-spinlock.h"
+
+/* The native Windows documentation says that CRITICAL_SECTION already
+   implements a recursive lock.  But we need not rely on it: It's easy to
+   implement a recursive lock without this assumption.  */
+
+typedef struct
+        {
+          glwthread_spinlock_t guard; /* protects the initialization */
+          DWORD owner;
+          unsigned long depth;
+          CRITICAL_SECTION lock;
+        }
+        glwthread_recmutex_t;
+
+#define GLWTHREAD_RECMUTEX_INIT { GLWTHREAD_SPINLOCK_INIT, 0, 0 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void glwthread_recmutex_init (glwthread_recmutex_t *mutex);
+extern int glwthread_recmutex_lock (glwthread_recmutex_t *mutex);
+extern int glwthread_recmutex_trylock (glwthread_recmutex_t *mutex);
+extern int glwthread_recmutex_unlock (glwthread_recmutex_t *mutex);
+extern int glwthread_recmutex_destroy (glwthread_recmutex_t *mutex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_RECMUTEX_H */
diff --git a/modules/lock b/modules/lock
index 351e902..0be823c 100644
--- a/modules/lock
+++ b/modules/lock
@@ -10,8 +10,9 @@ m4/pthread_rwlock_rdlock.m4
 Depends-on:
 extensions
 threadlib
-windows-mutex   [test $gl_threads_api = windows]
-windows-once    [test $gl_threads_api = windows]
+windows-mutex    [test $gl_threads_api = windows]
+windows-recmutex [test $gl_threads_api = windows]
+windows-once     [test $gl_threads_api = windows]
 
 configure.ac:
 gl_LOCK
diff --git a/modules/windows-recmutex b/modules/windows-recmutex
new file mode 100644
index 0000000..f8c0861
--- /dev/null
+++ b/modules/windows-recmutex
@@ -0,0 +1,28 @@
+Description:
+Plain recursive mutexes (native Windows implementation).
+
+Files:
+lib/windows-recmutex.h
+lib/windows-recmutex.c
+lib/windows-spinlock.h
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-recmutex])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-recmutex.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #12: 0011-windows-timedmutex-New-module.patch --]
[-- Type: text/x-patch, Size: 11318 bytes --]

From d20c7ec39fb2adbaf9bb3a3cc608019fee678870 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:11:57 +0200
Subject: [PATCH 11/26] windows-timedmutex: New module.

* lib/windows-timedmutex.h: New file, based on windows-mutex.h.
* lib/windows-timedmutex.c: New file, based on windows-mutex.c.
* modules/windows-timedmutex: New file.
---
 ChangeLog                  |   7 ++
 lib/windows-timedmutex.c   | 223 +++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-timedmutex.h   |  52 +++++++++++
 modules/windows-timedmutex |  31 +++++++
 4 files changed, 313 insertions(+)
 create mode 100644 lib/windows-timedmutex.c
 create mode 100644 lib/windows-timedmutex.h
 create mode 100644 modules/windows-timedmutex

diff --git a/ChangeLog b/ChangeLog
index 1ac5490..4038368 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-timedmutex: New module.
+	* lib/windows-timedmutex.h: New file, based on windows-mutex.h.
+	* lib/windows-timedmutex.c: New file, based on windows-mutex.c.
+	* modules/windows-timedmutex: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-recmutex: New module.
 	* lib/windows-recmutex.h: New file, extracted from lib/glthread/lock.h.
 	* lib/windows-recmutex.c: New file, extracted from lib/glthread/lock.c.
diff --git a/lib/windows-timedmutex.c b/lib/windows-timedmutex.c
new file mode 100644
index 0000000..20adbfb
--- /dev/null
+++ b/lib/windows-timedmutex.c
@@ -0,0 +1,223 @@
+/* Timed mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-timedmutex.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+int
+glwthread_timedmutex_init (glwthread_timedmutex_t *mutex)
+{
+  /* Attempt to allocate an auto-reset event object.  */
+  /* CreateEvent
+     <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
+  HANDLE event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  if (event == INVALID_HANDLE_VALUE)
+    return EAGAIN;
+  mutex->event = event;
+  InitializeCriticalSection (&mutex->lock);
+  mutex->guard.done = 1;
+  return 0;
+}
+
+int
+glwthread_timedmutex_lock (glwthread_timedmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+  EnterCriticalSection (&mutex->lock);
+  return 0;
+}
+
+int
+glwthread_timedmutex_trylock (glwthread_timedmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Let another thread finish initializing this mutex, and let it also
+             lock this mutex.  */
+          return EBUSY;
+        }
+    }
+  if (!TryEnterCriticalSection (&mutex->lock))
+    return EBUSY;
+  return 0;
+}
+
+int
+glwthread_timedmutex_timedlock (glwthread_timedmutex_t *mutex,
+                                const struct timespec *abstime)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+
+  /* POSIX says:
+      "Under no circumstance shall the function fail with a timeout if
+       the mutex can be locked immediately. The validity of the abstime
+       parameter need not be checked if the mutex can be locked
+       immediately."
+     Therefore start the loop with a TryEnterCriticalSection call.  */
+  for (;;)
+    {
+      if (TryEnterCriticalSection (&mutex->lock))
+        break;
+
+      {
+        struct timeval currtime;
+        DWORD timeout;
+        DWORD result;
+
+        gettimeofday (&currtime, NULL);
+
+        /* Wait until another thread signals the event or until the
+           abstime passes.  */
+        if (currtime.tv_sec > abstime->tv_sec)
+          timeout = 0;
+        else
+          {
+            unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+            timeout = seconds * 1000;
+            if (timeout / 1000 != seconds) /* overflow? */
+              timeout = INFINITE;
+            else
+              {
+                long milliseconds =
+                  abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
+                if (milliseconds >= 0)
+                  {
+                    timeout += milliseconds;
+                    if (timeout < milliseconds) /* overflow? */
+                      timeout = INFINITE;
+                  }
+                else
+                  {
+                    if (timeout >= - milliseconds)
+                      timeout -= (- milliseconds);
+                    else
+                      timeout = 0;
+                  }
+              }
+          }
+        if (timeout == 0)
+          return ETIMEDOUT;
+
+        /* WaitForSingleObject
+           <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
+        result = WaitForSingleObject (mutex->event, timeout);
+        if (result == WAIT_FAILED)
+          abort ();
+        if (result == WAIT_TIMEOUT)
+          return ETIMEDOUT;
+        /* Another thread has just unlocked the mutex.  We have good chances at
+           locking it now.  */
+      }
+    }
+  return 0;
+}
+
+int
+glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    return EINVAL;
+  LeaveCriticalSection (&mutex->lock);
+  /* Notify one of the threads that were waiting with a timeout.  */
+  /* SetEvent
+     <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
+  SetEvent (mutex->event);
+  return 0;
+}
+
+int
+glwthread_timedmutex_destroy (glwthread_timedmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    return EINVAL;
+  DeleteCriticalSection (&mutex->lock);
+  /* CloseHandle
+     <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
+  CloseHandle (mutex->event);
+  mutex->guard.done = 0;
+  return 0;
+}
diff --git a/lib/windows-timedmutex.h b/lib/windows-timedmutex.h
new file mode 100644
index 0000000..268c391
--- /dev/null
+++ b/lib/windows-timedmutex.h
@@ -0,0 +1,52 @@
+/* Timed mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_TIMEDMUTEX_H
+#define _WINDOWS_TIMEDMUTEX_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+#include <time.h>
+
+#include "windows-spinlock.h"
+
+typedef struct
+        {
+          glwthread_spinlock_t guard; /* protects the initialization */
+          HANDLE event;
+          CRITICAL_SECTION lock;
+        }
+        glwthread_timedmutex_t;
+
+#define GLWTHREAD_TIMEDMUTEX_INIT { GLWTHREAD_SPINLOCK_INIT }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_timedmutex_init (glwthread_timedmutex_t *mutex);
+extern int glwthread_timedmutex_lock (glwthread_timedmutex_t *mutex);
+extern int glwthread_timedmutex_trylock (glwthread_timedmutex_t *mutex);
+extern int glwthread_timedmutex_timedlock (glwthread_timedmutex_t *mutex,
+                                           const struct timespec *abstime);
+extern int glwthread_timedmutex_unlock (glwthread_timedmutex_t *mutex);
+extern int glwthread_timedmutex_destroy (glwthread_timedmutex_t *mutex);
+
+#endif /* _WINDOWS_TIMEDMUTEX_H */
diff --git a/modules/windows-timedmutex b/modules/windows-timedmutex
new file mode 100644
index 0000000..a9d053f
--- /dev/null
+++ b/modules/windows-timedmutex
@@ -0,0 +1,31 @@
+Description:
+Timed mutexes (native Windows implementation).
+
+Files:
+lib/windows-timedmutex.h
+lib/windows-timedmutex.c
+lib/windows-spinlock.h
+
+Depends-on:
+errno
+time
+gettimeofday
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-timedmutex])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-timedmutex.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #13: 0012-windows-timedrecmutex-New-module.patch --]
[-- Type: text/x-patch, Size: 13127 bytes --]

From f46ac2b2b4635118957a5718a125016f4ec15a8a Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:12:41 +0200
Subject: [PATCH 12/26] windows-timedrecmutex: New module.

* lib/windows-timedrecmutex.h: New file, based on windows-recmutex.h.
* lib/windows-timedrecmutex.c: New file, based on windows-recmutex.c.
* modules/windows-timedrecmutex: New file.
---
 ChangeLog                     |   7 ++
 lib/windows-timedrecmutex.c   | 267 ++++++++++++++++++++++++++++++++++++++++++
 lib/windows-timedrecmutex.h   |  62 ++++++++++
 modules/windows-timedrecmutex |  31 +++++
 4 files changed, 367 insertions(+)
 create mode 100644 lib/windows-timedrecmutex.c
 create mode 100644 lib/windows-timedrecmutex.h
 create mode 100644 modules/windows-timedrecmutex

diff --git a/ChangeLog b/ChangeLog
index 4038368..5e416fe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-timedrecmutex: New module.
+	* lib/windows-timedrecmutex.h: New file, based on windows-recmutex.h.
+	* lib/windows-timedrecmutex.c: New file, based on windows-recmutex.c.
+	* modules/windows-timedrecmutex: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-timedmutex: New module.
 	* lib/windows-timedmutex.h: New file, based on windows-mutex.h.
 	* lib/windows-timedmutex.c: New file, based on windows-mutex.c.
diff --git a/lib/windows-timedrecmutex.c b/lib/windows-timedrecmutex.c
new file mode 100644
index 0000000..6f91d46
--- /dev/null
+++ b/lib/windows-timedrecmutex.c
@@ -0,0 +1,267 @@
+/* Timed recursive mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-timedrecmutex.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+int
+glwthread_timedrecmutex_init (glwthread_timedrecmutex_t *mutex)
+{
+  mutex->owner = 0;
+  mutex->depth = 0;
+  /* Attempt to allocate an auto-reset event object.  */
+  /* CreateEvent
+     <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createeventa> */
+  HANDLE event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  if (event == INVALID_HANDLE_VALUE)
+    return EAGAIN;
+  mutex->event = event;
+  InitializeCriticalSection (&mutex->lock);
+  mutex->guard.done = 1;
+  return 0;
+}
+
+int
+glwthread_timedrecmutex_lock (glwthread_timedrecmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedrecmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+  {
+    DWORD self = GetCurrentThreadId ();
+    if (mutex->owner != self)
+      {
+        EnterCriticalSection (&mutex->lock);
+        mutex->owner = self;
+      }
+    if (++(mutex->depth) == 0) /* wraparound? */
+      {
+        mutex->depth--;
+        return EAGAIN;
+      }
+  }
+  return 0;
+}
+
+int
+glwthread_timedrecmutex_trylock (glwthread_timedrecmutex_t *mutex)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedrecmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Let another thread finish initializing this mutex, and let it also
+             lock this mutex.  */
+          return EBUSY;
+        }
+    }
+  {
+    DWORD self = GetCurrentThreadId ();
+    if (mutex->owner != self)
+      {
+        if (!TryEnterCriticalSection (&mutex->lock))
+          return EBUSY;
+        mutex->owner = self;
+      }
+    if (++(mutex->depth) == 0) /* wraparound? */
+      {
+        mutex->depth--;
+        return EAGAIN;
+      }
+  }
+  return 0;
+}
+
+int
+glwthread_timedrecmutex_timedlock (glwthread_timedrecmutex_t *mutex,
+                                   const struct timespec *abstime)
+{
+  if (!mutex->guard.done)
+    {
+      if (InterlockedIncrement (&mutex->guard.started) == 0)
+        {
+          /* This thread is the first one to need this mutex.
+             Initialize it.  */
+          int err = glwthread_timedrecmutex_init (mutex);
+          if (err != 0)
+            {
+              /* Undo increment.  */
+              InterlockedDecrement (&mutex->guard.started);
+              return err;
+            }
+        }
+      else
+        {
+          /* Don't let mutex->guard.started grow and wrap around.  */
+          InterlockedDecrement (&mutex->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this mutex.  */
+          while (!mutex->guard.done)
+            Sleep (0);
+        }
+    }
+
+  {
+    DWORD self = GetCurrentThreadId ();
+    if (mutex->owner != self)
+      {
+        /* POSIX says:
+            "Under no circumstance shall the function fail with a timeout if
+             the mutex can be locked immediately. The validity of the abstime
+             parameter need not be checked if the mutex can be locked
+             immediately."
+           Therefore start the loop with a TryEnterCriticalSection call.  */
+        for (;;)
+          {
+            if (TryEnterCriticalSection (&mutex->lock))
+              break;
+
+            {
+              struct timeval currtime;
+              DWORD timeout;
+              DWORD result;
+
+              gettimeofday (&currtime, NULL);
+
+              /* Wait until another thread signals the event or until the
+                 abstime passes.  */
+              if (currtime.tv_sec > abstime->tv_sec)
+                timeout = 0;
+              else
+                {
+                  unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+                  timeout = seconds * 1000;
+                  if (timeout / 1000 != seconds) /* overflow? */
+                    timeout = INFINITE;
+                  else
+                    {
+                      long milliseconds =
+                        abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
+                      if (milliseconds >= 0)
+                        {
+                          timeout += milliseconds;
+                          if (timeout < milliseconds) /* overflow? */
+                            timeout = INFINITE;
+                        }
+                      else
+                        {
+                          if (timeout >= - milliseconds)
+                            timeout -= (- milliseconds);
+                          else
+                            timeout = 0;
+                        }
+                    }
+                }
+              if (timeout == 0)
+                return ETIMEDOUT;
+
+              /* WaitForSingleObject
+                 <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject> */
+              result = WaitForSingleObject (mutex->event, timeout);
+              if (result == WAIT_FAILED)
+                abort ();
+              if (result == WAIT_TIMEOUT)
+                return ETIMEDOUT;
+              /* Another thread has just unlocked the mutex.  We have good chances at
+                 locking it now.  */
+            }
+          }
+        mutex->owner = self;
+      }
+    if (++(mutex->depth) == 0) /* wraparound? */
+      {
+        mutex->depth--;
+        return EAGAIN;
+      }
+  }
+  return 0;
+}
+
+int
+glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t *mutex)
+{
+  if (mutex->owner != GetCurrentThreadId ())
+    return EPERM;
+  if (mutex->depth == 0)
+    return EINVAL;
+  if (--(mutex->depth) == 0)
+    {
+      mutex->owner = 0;
+      LeaveCriticalSection (&mutex->lock);
+      /* Notify one of the threads that were waiting with a timeout.  */
+      /* SetEvent
+         <https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-setevent> */
+      SetEvent (mutex->event);
+    }
+  return 0;
+}
+
+int
+glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t *mutex)
+{
+  if (mutex->owner != 0)
+    return EBUSY;
+  DeleteCriticalSection (&mutex->lock);
+  /* CloseHandle
+     <https://docs.microsoft.com/en-us/windows/desktop/api/handleapi/nf-handleapi-closehandle> */
+  CloseHandle (mutex->event);
+  mutex->guard.done = 0;
+  return 0;
+}
diff --git a/lib/windows-timedrecmutex.h b/lib/windows-timedrecmutex.h
new file mode 100644
index 0000000..c0d2d3b
--- /dev/null
+++ b/lib/windows-timedrecmutex.h
@@ -0,0 +1,62 @@
+/* Timed recursive mutexes (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_TIMEDRECMUTEX_H
+#define _WINDOWS_TIMEDRECMUTEX_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+#include <time.h>
+
+#include "windows-spinlock.h"
+
+/* The native Windows documentation says that CRITICAL_SECTION already
+   implements a recursive lock.  But we need not rely on it: It's easy to
+   implement a recursive lock without this assumption.  */
+
+typedef struct
+        {
+          glwthread_spinlock_t guard; /* protects the initialization */
+          DWORD owner;
+          unsigned long depth;
+          HANDLE event;
+          CRITICAL_SECTION lock;
+        }
+        glwthread_timedrecmutex_t;
+
+#define GLWTHREAD_TIMEDRECMUTEX_INIT { GLWTHREAD_SPINLOCK_INIT, 0, 0 }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_timedrecmutex_init (glwthread_timedrecmutex_t *mutex);
+extern int glwthread_timedrecmutex_lock (glwthread_timedrecmutex_t *mutex);
+extern int glwthread_timedrecmutex_trylock (glwthread_timedrecmutex_t *mutex);
+extern int glwthread_timedrecmutex_timedlock (glwthread_timedrecmutex_t *mutex,
+                                              const struct timespec *abstime);
+extern int glwthread_timedrecmutex_unlock (glwthread_timedrecmutex_t *mutex);
+extern int glwthread_timedrecmutex_destroy (glwthread_timedrecmutex_t *mutex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_TIMEDRECMUTEX_H */
diff --git a/modules/windows-timedrecmutex b/modules/windows-timedrecmutex
new file mode 100644
index 0000000..116817a
--- /dev/null
+++ b/modules/windows-timedrecmutex
@@ -0,0 +1,31 @@
+Description:
+Timed recursive mutexes (native Windows implementation).
+
+Files:
+lib/windows-timedrecmutex.h
+lib/windows-timedrecmutex.c
+lib/windows-spinlock.h
+
+Depends-on:
+errno
+time
+gettimeofday
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-timedrecmutex])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-timedrecmutex.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #14: 0013-windows-cond-New-module.patch --]
[-- Type: text/x-patch, Size: 39035 bytes --]

From 7b7305f41af7c79ab3739704febba7cf171b404a Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:13:43 +0200
Subject: [PATCH 13/26] windows-cond: New module.

* lib/windows-cond.h: New file, based on lib/glthread/cond.h.
* lib/windows-cond.c: New file, based on lib/glthread/cond.c.
* lib/glthread/cond.h: Include windows-cond.h.
(struct gl_waitqueue_link, gl_linked_waitqueue_t): Remove types.
(gl_cond_t): Define using glwthread_cond_t.
(gl_cond_initializer): Define using GLWTHREAD_COND_INIT.
(glthread_cond_init): Define using glwthread_cond_init.
(glthread_cond_wait): Define using glwthread_cond_wait.
(glthread_cond_timedwait): Define using glwthread_cond_timedwait.
(glthread_cond_signal): Define using glwthread_cond_signal.
(glthread_cond_broadcast): Define using glwthread_cond_broadcast.
(glthread_cond_destroy): Define using glwthread_cond_destroy.
(glthread_cond_init_func, glthread_cond_wait_func,
glthread_cond_timedwait_func, glthread_cond_signal_func,
glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
declarations.
* lib/glthread/cond.c (gl_waitqueue_t, gl_waitqueue_element): Remove
types.
(gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove,
gl_waitqueue_notify_first, gl_waitqueue_notify_all,
glthread_cond_init_func, glthread_cond_wait_func,
glthread_cond_timedwait_func, glthread_cond_signal_func,
glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
functions.
* modules/windows-cond: New file.
* modules/cond (Depends-on): Add windows-cond. Remove gettimeofday.
---
 ChangeLog            |  30 ++++
 lib/glthread/cond.c  | 394 -----------------------------------------------
 lib/glthread/cond.h  |  45 ++----
 lib/windows-cond.c   | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-cond.h   |  74 +++++++++
 modules/cond         |   2 +-
 modules/windows-cond |  32 ++++
 7 files changed, 577 insertions(+), 425 deletions(-)
 create mode 100644 lib/windows-cond.c
 create mode 100644 lib/windows-cond.h
 create mode 100644 modules/windows-cond

diff --git a/ChangeLog b/ChangeLog
index 5e416fe..6307fcc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,35 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-cond: New module.
+	* lib/windows-cond.h: New file, based on lib/glthread/cond.h.
+	* lib/windows-cond.c: New file, based on lib/glthread/cond.c.
+	* lib/glthread/cond.h: Include windows-cond.h.
+	(struct gl_waitqueue_link, gl_linked_waitqueue_t): Remove types.
+	(gl_cond_t): Define using glwthread_cond_t.
+	(gl_cond_initializer): Define using GLWTHREAD_COND_INIT.
+	(glthread_cond_init): Define using glwthread_cond_init.
+	(glthread_cond_wait): Define using glwthread_cond_wait.
+	(glthread_cond_timedwait): Define using glwthread_cond_timedwait.
+	(glthread_cond_signal): Define using glwthread_cond_signal.
+	(glthread_cond_broadcast): Define using glwthread_cond_broadcast.
+	(glthread_cond_destroy): Define using glwthread_cond_destroy.
+	(glthread_cond_init_func, glthread_cond_wait_func,
+	glthread_cond_timedwait_func, glthread_cond_signal_func,
+	glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
+	declarations.
+	* lib/glthread/cond.c (gl_waitqueue_t, gl_waitqueue_element): Remove
+	types.
+	(gl_waitqueue_init, gl_waitqueue_add, gl_waitqueue_remove,
+	gl_waitqueue_notify_first, gl_waitqueue_notify_all,
+	glthread_cond_init_func, glthread_cond_wait_func,
+	glthread_cond_timedwait_func, glthread_cond_signal_func,
+	glthread_cond_broadcast_func, glthread_cond_destroy_func): Remove
+	functions.
+	* modules/windows-cond: New file.
+	* modules/cond (Depends-on): Add windows-cond. Remove gettimeofday.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-timedrecmutex: New module.
 	* lib/windows-timedrecmutex.h: New file, based on windows-recmutex.h.
 	* lib/windows-timedrecmutex.c: New file, based on windows-recmutex.c.
diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c
index 6df6780..2285c16 100644
--- a/lib/glthread/cond.c
+++ b/lib/glthread/cond.c
@@ -75,400 +75,6 @@ glthread_cond_timedwait_multithreaded (gl_cond_t *cond,
 
 #if USE_WINDOWS_THREADS
 
-#include <sys/time.h>
-
-/* -------------------------- gl_cond_t datatype -------------------------- */
-
-/* In this file, the waitqueues are implemented as linked lists.  */
-#define gl_waitqueue_t gl_linked_waitqueue_t
-
-/* All links of a circular list, except the anchor, are of this type, carrying
-   a payload.  */
-struct gl_waitqueue_element
-{
-  struct gl_waitqueue_link link; /* must be the first field! */
-  HANDLE event; /* Waiting thread, represented by an event.
-                   This field is immutable once initialized. */
-};
-
-static void
-gl_waitqueue_init (gl_waitqueue_t *wq)
-{
-  wq->wq_list.wql_next = &wq->wq_list;
-  wq->wq_list.wql_prev = &wq->wq_list;
-}
-
-/* Enqueues the current thread, represented by an event, in a wait queue.
-   Returns NULL if an allocation failure occurs.  */
-static struct gl_waitqueue_element *
-gl_waitqueue_add (gl_waitqueue_t *wq)
-{
-  struct gl_waitqueue_element *elt;
-  HANDLE event;
-
-  /* Allocate the memory for the waitqueue element on the heap, not on the
-     thread's stack.  If the thread exits unexpectedly, we prefer to leak
-     some memory rather than to access unavailable memory and crash.  */
-  elt =
-    (struct gl_waitqueue_element *)
-    malloc (sizeof (struct gl_waitqueue_element));
-  if (elt == NULL)
-    /* No more memory.  */
-    return NULL;
-
-  /* Whether the created event is a manual-reset one or an auto-reset one,
-     does not matter, since we will wait on it only once.  */
-  event = CreateEvent (NULL, TRUE, FALSE, NULL);
-  if (event == INVALID_HANDLE_VALUE)
-    {
-      /* No way to allocate an event.  */
-      free (elt);
-      return NULL;
-    }
-  elt->event = event;
-  /* Insert elt at the end of the circular list.  */
-  (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
-  (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
-  return elt;
-}
-
-/* Removes the current thread, represented by a 'struct gl_waitqueue_element *',
-   from a wait queue.
-   Returns true if is was found and removed, false if it was not present.  */
-static bool
-gl_waitqueue_remove (gl_waitqueue_t *wq, struct gl_waitqueue_element *elt)
-{
-  if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
-    {
-      /* Remove elt from the circular list.  */
-      struct gl_waitqueue_link *prev = elt->link.wql_prev;
-      struct gl_waitqueue_link *next = elt->link.wql_next;
-      prev->wql_next = next;
-      next->wql_prev = prev;
-      elt->link.wql_next = NULL;
-      elt->link.wql_prev = NULL;
-      return true;
-    }
-  else
-    return false;
-}
-
-/* Notifies the first thread from a wait queue and dequeues it.  */
-static void
-gl_waitqueue_notify_first (gl_waitqueue_t *wq)
-{
-  if (wq->wq_list.wql_next != &wq->wq_list)
-    {
-      struct gl_waitqueue_element *elt =
-        (struct gl_waitqueue_element *) wq->wq_list.wql_next;
-      struct gl_waitqueue_link *prev;
-      struct gl_waitqueue_link *next;
-
-      /* Remove elt from the circular list.  */
-      prev = &wq->wq_list; /* = elt->link.wql_prev; */
-      next = elt->link.wql_next;
-      prev->wql_next = next;
-      next->wql_prev = prev;
-      elt->link.wql_next = NULL;
-      elt->link.wql_prev = NULL;
-
-      SetEvent (elt->event);
-      /* After the SetEvent, this thread cannot access *elt any more, because
-         the woken-up thread will quickly call  free (elt).  */
-    }
-}
-
-/* Notifies all threads from a wait queue and dequeues them all.  */
-static void
-gl_waitqueue_notify_all (gl_waitqueue_t *wq)
-{
-  struct gl_waitqueue_link *l;
-
-  for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
-    {
-      struct gl_waitqueue_element *elt = (struct gl_waitqueue_element *) l;
-      struct gl_waitqueue_link *prev;
-      struct gl_waitqueue_link *next;
-
-      /* Remove elt from the circular list.  */
-      prev = &wq->wq_list; /* = elt->link.wql_prev; */
-      next = elt->link.wql_next;
-      prev->wql_next = next;
-      next->wql_prev = prev;
-      elt->link.wql_next = NULL;
-      elt->link.wql_prev = NULL;
-
-      SetEvent (elt->event);
-      /* After the SetEvent, this thread cannot access *elt any more, because
-         the woken-up thread will quickly call  free (elt).  */
-
-      l = next;
-    }
-  if (!(wq->wq_list.wql_next == &wq->wq_list
-        && wq->wq_list.wql_prev == &wq->wq_list))
-    abort ();
-}
-
-int
-glthread_cond_init_func (gl_cond_t *cond)
-{
-  InitializeCriticalSection (&cond->lock);
-  gl_waitqueue_init (&cond->waiters);
-
-  cond->guard.done = 1;
-  return 0;
-}
-
-int
-glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock)
-{
-  if (!cond->guard.done)
-    {
-      if (InterlockedIncrement (&cond->guard.started) == 0)
-        /* This thread is the first one to need this condition variable.
-           Initialize it.  */
-        glthread_cond_init (cond);
-      else
-        {
-          /* Don't let cond->guard.started grow and wrap around.  */
-          InterlockedDecrement (&cond->guard.started);
-          /* Yield the CPU while waiting for another thread to finish
-             initializing this condition variable.  */
-          while (!cond->guard.done)
-            Sleep (0);
-        }
-    }
-
-  EnterCriticalSection (&cond->lock);
-  {
-    struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
-    LeaveCriticalSection (&cond->lock);
-    if (elt == NULL)
-      {
-        /* Allocation failure.  Weird.  */
-        return EAGAIN;
-      }
-    else
-      {
-        HANDLE event = elt->event;
-        int err;
-        DWORD result;
-
-        /* Now release the lock and let any other thread take it.  */
-        err = glthread_lock_unlock (lock);
-        if (err != 0)
-          {
-            EnterCriticalSection (&cond->lock);
-            gl_waitqueue_remove (&cond->waiters, elt);
-            LeaveCriticalSection (&cond->lock);
-            CloseHandle (event);
-            free (elt);
-            return err;
-          }
-        /* POSIX says:
-            "If another thread is able to acquire the mutex after the
-             about-to-block thread has released it, then a subsequent call to
-             pthread_cond_broadcast() or pthread_cond_signal() in that thread
-             shall behave as if it were issued after the about-to-block thread
-             has blocked."
-           This is fulfilled here, because the thread signalling is done
-           through SetEvent, not PulseEvent.  */
-        /* Wait until another thread signals this event.  */
-        result = WaitForSingleObject (event, INFINITE);
-        if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
-          abort ();
-        CloseHandle (event);
-        free (elt);
-        /* The thread which signalled the event already did the bookkeeping:
-           removed us from the waiters.  */
-        return glthread_lock_lock (lock);
-      }
-  }
-}
-
-int
-glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime)
-{
-  if (!cond->guard.done)
-    {
-      if (InterlockedIncrement (&cond->guard.started) == 0)
-        /* This thread is the first one to need this condition variable.
-           Initialize it.  */
-        glthread_cond_init (cond);
-      else
-        {
-          /* Don't let cond->guard.started grow and wrap around.  */
-          InterlockedDecrement (&cond->guard.started);
-          /* Yield the CPU while waiting for another thread to finish
-             initializing this condition variable.  */
-          while (!cond->guard.done)
-            Sleep (0);
-        }
-    }
-
-  {
-    struct timeval currtime;
-
-    gettimeofday (&currtime, NULL);
-    if (currtime.tv_sec > abstime->tv_sec
-        || (currtime.tv_sec == abstime->tv_sec
-            && currtime.tv_usec * 1000 >= abstime->tv_nsec))
-      return ETIMEDOUT;
-
-    EnterCriticalSection (&cond->lock);
-    {
-      struct gl_waitqueue_element *elt = gl_waitqueue_add (&cond->waiters);
-      LeaveCriticalSection (&cond->lock);
-      if (elt == NULL)
-        {
-          /* Allocation failure.  Weird.  */
-          return EAGAIN;
-        }
-      else
-        {
-          HANDLE event = elt->event;
-          int err;
-          DWORD timeout;
-          DWORD result;
-
-          /* Now release the lock and let any other thread take it.  */
-          err = glthread_lock_unlock (lock);
-          if (err != 0)
-            {
-              EnterCriticalSection (&cond->lock);
-              gl_waitqueue_remove (&cond->waiters, elt);
-              LeaveCriticalSection (&cond->lock);
-              CloseHandle (event);
-              free (elt);
-              return err;
-            }
-          /* POSIX says:
-              "If another thread is able to acquire the mutex after the
-               about-to-block thread has released it, then a subsequent call to
-               pthread_cond_broadcast() or pthread_cond_signal() in that thread
-               shall behave as if it were issued after the about-to-block thread
-               has blocked."
-             This is fulfilled here, because the thread signalling is done
-             through SetEvent, not PulseEvent.  */
-          /* Wait until another thread signals this event or until the abstime
-             passes.  */
-          gettimeofday (&currtime, NULL);
-          if (currtime.tv_sec > abstime->tv_sec)
-            timeout = 0;
-          else
-            {
-              unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
-              timeout = seconds * 1000;
-              if (timeout / 1000 != seconds) /* overflow? */
-                timeout = INFINITE;
-              else
-                {
-                  long milliseconds =
-                    abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
-                  if (milliseconds >= 0)
-                    {
-                      timeout += milliseconds;
-                      if (timeout < milliseconds) /* overflow? */
-                        timeout = INFINITE;
-                    }
-                  else
-                    {
-                      if (timeout >= - milliseconds)
-                        timeout -= (- milliseconds);
-                      else
-                        timeout = 0;
-                    }
-                }
-            }
-          result = WaitForSingleObject (event, timeout);
-          if (result == WAIT_FAILED)
-            abort ();
-          if (result == WAIT_TIMEOUT)
-            {
-              EnterCriticalSection (&cond->lock);
-              if (gl_waitqueue_remove (&cond->waiters, elt))
-                {
-                  /* The event was not signaled between the WaitForSingleObject
-                     call and the EnterCriticalSection call.  */
-                  if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
-                    abort ();
-                }
-              else
-                {
-                  /* The event was signaled between the WaitForSingleObject
-                     call and the EnterCriticalSection call.  */
-                  if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
-                    abort ();
-                  /* Produce the right return value.  */
-                  result = WAIT_OBJECT_0;
-                }
-              LeaveCriticalSection (&cond->lock);
-            }
-          else
-            {
-              /* The thread which signalled the event already did the
-                 bookkeeping: removed us from the waiters.  */
-            }
-          CloseHandle (event);
-          free (elt);
-          /* Take the lock again.  It does not matter whether this is done
-             before or after the bookkeeping for WAIT_TIMEOUT.  */
-          err = glthread_lock_lock (lock);
-          return (err ? err :
-                  result == WAIT_OBJECT_0 ? 0 :
-                  result == WAIT_TIMEOUT ? ETIMEDOUT :
-                  /* WAIT_FAILED shouldn't happen */ EAGAIN);
-        }
-    }
-  }
-}
-
-int
-glthread_cond_signal_func (gl_cond_t *cond)
-{
-  if (!cond->guard.done)
-    return EINVAL;
-
-  EnterCriticalSection (&cond->lock);
-  /* POSIX says:
-      "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
-       have no effect if there are no threads currently blocked on cond."  */
-  if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
-    gl_waitqueue_notify_first (&cond->waiters);
-  LeaveCriticalSection (&cond->lock);
-
-  return 0;
-}
-
-int
-glthread_cond_broadcast_func (gl_cond_t *cond)
-{
-  if (!cond->guard.done)
-    return EINVAL;
-
-  EnterCriticalSection (&cond->lock);
-  /* POSIX says:
-      "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
-       have no effect if there are no threads currently blocked on cond."
-     gl_waitqueue_notify_all is a nop in this case.  */
-  gl_waitqueue_notify_all (&cond->waiters);
-  LeaveCriticalSection (&cond->lock);
-
-  return 0;
-}
-
-int
-glthread_cond_destroy_func (gl_cond_t *cond)
-{
-  if (!cond->guard.done)
-    return EINVAL;
-  if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
-    return EBUSY;
-  DeleteCriticalSection (&cond->lock);
-  cond->guard.done = 0;
-  return 0;
-}
-
 #endif
 
 /* ========================================================================= */
diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h
index f252c32..8880b07 100644
--- a/lib/glthread/cond.h
+++ b/lib/glthread/cond.h
@@ -293,53 +293,38 @@ extern int glthread_cond_timedwait_multithreaded (gl_cond_t *cond, gl_lock_t *lo
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 # include <windows.h>
 
+# include "windows-cond.h"
+
 # ifdef __cplusplus
 extern "C" {
 # endif
 
 /* -------------------------- gl_cond_t datatype -------------------------- */
 
-struct gl_waitqueue_link
-{
-  struct gl_waitqueue_link *wql_next;
-  struct gl_waitqueue_link *wql_prev;
-};
-typedef struct
-        {
-          struct gl_waitqueue_link wq_list; /* circular list of waiting threads */
-        }
-        gl_linked_waitqueue_t;
-typedef struct
-        {
-          glwthread_spinlock_t guard; /* protects the initialization */
-          CRITICAL_SECTION lock; /* protects the remaining fields */
-          gl_linked_waitqueue_t waiters; /* waiting threads */
-        }
-        gl_cond_t;
+typedef glwthread_cond_t gl_cond_t;
 # define gl_cond_define(STORAGECLASS, NAME) \
     STORAGECLASS gl_cond_t NAME;
 # define gl_cond_define_initialized(STORAGECLASS, NAME) \
     STORAGECLASS gl_cond_t NAME = gl_cond_initializer;
 # define gl_cond_initializer \
-    { { 0, -1 } }
+    GLWTHREAD_COND_INIT
 # define glthread_cond_init(COND) \
-    glthread_cond_init_func (COND)
+    glwthread_cond_init (COND)
 # define glthread_cond_wait(COND, LOCK) \
-    glthread_cond_wait_func (COND, LOCK)
+    glwthread_cond_wait (COND, LOCK, \
+                         (int (*) (void *)) glwthread_mutex_lock, \
+                         (int (*) (void *)) glwthread_mutex_unlock)
 # define glthread_cond_timedwait(COND, LOCK, ABSTIME) \
-    glthread_cond_timedwait_func (COND, LOCK, ABSTIME)
+    glwthread_cond_timedwait (COND, LOCK, \
+                              (int (*) (void *)) glwthread_mutex_lock, \
+                              (int (*) (void *)) glwthread_mutex_unlock, \
+                              ABSTIME)
 # define glthread_cond_signal(COND) \
-    glthread_cond_signal_func (COND)
+    glwthread_cond_signal (COND)
 # define glthread_cond_broadcast(COND) \
-    glthread_cond_broadcast_func (COND)
+    glwthread_cond_broadcast (COND)
 # define glthread_cond_destroy(COND) \
-    glthread_cond_destroy_func (COND)
-extern int glthread_cond_init_func (gl_cond_t *cond);
-extern int glthread_cond_wait_func (gl_cond_t *cond, gl_lock_t *lock);
-extern int glthread_cond_timedwait_func (gl_cond_t *cond, gl_lock_t *lock, struct timespec *abstime);
-extern int glthread_cond_signal_func (gl_cond_t *cond);
-extern int glthread_cond_broadcast_func (gl_cond_t *cond);
-extern int glthread_cond_destroy_func (gl_cond_t *cond);
+    glwthread_cond_destroy (COND)
 
 # ifdef __cplusplus
 }
diff --git a/lib/windows-cond.c b/lib/windows-cond.c
new file mode 100644
index 0000000..1c1c68a
--- /dev/null
+++ b/lib/windows-cond.c
@@ -0,0 +1,425 @@
+/* Condition variables (native Windows implementation).
+   Copyright (C) 2008-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
+   and Bruno Haible <bruno@clisp.org>, 2008.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-cond.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+/* In this file, the waitqueues are implemented as linked lists.  */
+#define glwthread_waitqueue_t glwthread_linked_waitqueue_t
+
+/* All links of a circular list, except the anchor, are of this type, carrying
+   a payload.  */
+struct glwthread_waitqueue_element
+{
+  struct glwthread_waitqueue_link link; /* must be the first field! */
+  HANDLE event; /* Waiting thread, represented by an event.
+                   This field is immutable once initialized. */
+};
+
+static void
+glwthread_waitqueue_init (glwthread_waitqueue_t *wq)
+{
+  wq->wq_list.wql_next = &wq->wq_list;
+  wq->wq_list.wql_prev = &wq->wq_list;
+}
+
+/* Enqueues the current thread, represented by an event, in a wait queue.
+   Returns NULL if an allocation failure occurs.  */
+static struct glwthread_waitqueue_element *
+glwthread_waitqueue_add (glwthread_waitqueue_t *wq)
+{
+  struct glwthread_waitqueue_element *elt;
+  HANDLE event;
+
+  /* Allocate the memory for the waitqueue element on the heap, not on the
+     thread's stack.  If the thread exits unexpectedly, we prefer to leak
+     some memory rather than to access unavailable memory and crash.  */
+  elt =
+    (struct glwthread_waitqueue_element *)
+    malloc (sizeof (struct glwthread_waitqueue_element));
+  if (elt == NULL)
+    /* No more memory.  */
+    return NULL;
+
+  /* Whether the created event is a manual-reset one or an auto-reset one,
+     does not matter, since we will wait on it only once.  */
+  event = CreateEvent (NULL, TRUE, FALSE, NULL);
+  if (event == INVALID_HANDLE_VALUE)
+    {
+      /* No way to allocate an event.  */
+      free (elt);
+      return NULL;
+    }
+  elt->event = event;
+  /* Insert elt at the end of the circular list.  */
+  (elt->link.wql_prev = wq->wq_list.wql_prev)->wql_next = &elt->link;
+  (elt->link.wql_next = &wq->wq_list)->wql_prev = &elt->link;
+  return elt;
+}
+
+/* Removes the current thread, represented by a
+   'struct glwthread_waitqueue_element *', from a wait queue.
+   Returns true if is was found and removed, false if it was not present.  */
+static bool
+glwthread_waitqueue_remove (glwthread_waitqueue_t *wq,
+                            struct glwthread_waitqueue_element *elt)
+{
+  if (elt->link.wql_next != NULL && elt->link.wql_prev != NULL)
+    {
+      /* Remove elt from the circular list.  */
+      struct glwthread_waitqueue_link *prev = elt->link.wql_prev;
+      struct glwthread_waitqueue_link *next = elt->link.wql_next;
+      prev->wql_next = next;
+      next->wql_prev = prev;
+      elt->link.wql_next = NULL;
+      elt->link.wql_prev = NULL;
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Notifies the first thread from a wait queue and dequeues it.  */
+static void
+glwthread_waitqueue_notify_first (glwthread_waitqueue_t *wq)
+{
+  if (wq->wq_list.wql_next != &wq->wq_list)
+    {
+      struct glwthread_waitqueue_element *elt =
+        (struct glwthread_waitqueue_element *) wq->wq_list.wql_next;
+      struct glwthread_waitqueue_link *prev;
+      struct glwthread_waitqueue_link *next;
+
+      /* Remove elt from the circular list.  */
+      prev = &wq->wq_list; /* = elt->link.wql_prev; */
+      next = elt->link.wql_next;
+      prev->wql_next = next;
+      next->wql_prev = prev;
+      elt->link.wql_next = NULL;
+      elt->link.wql_prev = NULL;
+
+      SetEvent (elt->event);
+      /* After the SetEvent, this thread cannot access *elt any more, because
+         the woken-up thread will quickly call  free (elt).  */
+    }
+}
+
+/* Notifies all threads from a wait queue and dequeues them all.  */
+static void
+glwthread_waitqueue_notify_all (glwthread_waitqueue_t *wq)
+{
+  struct glwthread_waitqueue_link *l;
+
+  for (l = wq->wq_list.wql_next; l != &wq->wq_list; )
+    {
+      struct glwthread_waitqueue_element *elt =
+        (struct glwthread_waitqueue_element *) l;
+      struct glwthread_waitqueue_link *prev;
+      struct glwthread_waitqueue_link *next;
+
+      /* Remove elt from the circular list.  */
+      prev = &wq->wq_list; /* = elt->link.wql_prev; */
+      next = elt->link.wql_next;
+      prev->wql_next = next;
+      next->wql_prev = prev;
+      elt->link.wql_next = NULL;
+      elt->link.wql_prev = NULL;
+
+      SetEvent (elt->event);
+      /* After the SetEvent, this thread cannot access *elt any more, because
+         the woken-up thread will quickly call  free (elt).  */
+
+      l = next;
+    }
+  if (!(wq->wq_list.wql_next == &wq->wq_list
+        && wq->wq_list.wql_prev == &wq->wq_list))
+    abort ();
+}
+
+int
+glwthread_cond_init (glwthread_cond_t *cond)
+{
+  InitializeCriticalSection (&cond->lock);
+  glwthread_waitqueue_init (&cond->waiters);
+
+  cond->guard.done = 1;
+  return 0;
+}
+
+int
+glwthread_cond_wait (glwthread_cond_t *cond,
+                     void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *))
+{
+  if (!cond->guard.done)
+    {
+      if (InterlockedIncrement (&cond->guard.started) == 0)
+        /* This thread is the first one to need this condition variable.
+           Initialize it.  */
+        glwthread_cond_init (cond);
+      else
+        {
+          /* Don't let cond->guard.started grow and wrap around.  */
+          InterlockedDecrement (&cond->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this condition variable.  */
+          while (!cond->guard.done)
+            Sleep (0);
+        }
+    }
+
+  EnterCriticalSection (&cond->lock);
+  {
+    struct glwthread_waitqueue_element *elt =
+      glwthread_waitqueue_add (&cond->waiters);
+    LeaveCriticalSection (&cond->lock);
+    if (elt == NULL)
+      {
+        /* Allocation failure.  Weird.  */
+        return EAGAIN;
+      }
+    else
+      {
+        HANDLE event = elt->event;
+        int err;
+        DWORD result;
+
+        /* Now release the mutex and let any other thread take it.  */
+        err = mutex_unlock (mutex);
+        if (err != 0)
+          {
+            EnterCriticalSection (&cond->lock);
+            glwthread_waitqueue_remove (&cond->waiters, elt);
+            LeaveCriticalSection (&cond->lock);
+            CloseHandle (event);
+            free (elt);
+            return err;
+          }
+        /* POSIX says:
+            "If another thread is able to acquire the mutex after the
+             about-to-block thread has released it, then a subsequent call to
+             pthread_cond_broadcast() or pthread_cond_signal() in that thread
+             shall behave as if it were issued after the about-to-block thread
+             has blocked."
+           This is fulfilled here, because the thread signalling is done
+           through SetEvent, not PulseEvent.  */
+        /* Wait until another thread signals this event.  */
+        result = WaitForSingleObject (event, INFINITE);
+        if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+          abort ();
+        CloseHandle (event);
+        free (elt);
+        /* The thread which signalled the event already did the bookkeeping:
+           removed us from the waiters.  */
+        return mutex_lock (mutex);
+      }
+  }
+}
+
+int
+glwthread_cond_timedwait (glwthread_cond_t *cond,
+                          void *mutex, int (*mutex_lock) (void *), int (*mutex_unlock) (void *),
+                          const struct timespec *abstime)
+{
+  if (!cond->guard.done)
+    {
+      if (InterlockedIncrement (&cond->guard.started) == 0)
+        /* This thread is the first one to need this condition variable.
+           Initialize it.  */
+        glwthread_cond_init (cond);
+      else
+        {
+          /* Don't let cond->guard.started grow and wrap around.  */
+          InterlockedDecrement (&cond->guard.started);
+          /* Yield the CPU while waiting for another thread to finish
+             initializing this condition variable.  */
+          while (!cond->guard.done)
+            Sleep (0);
+        }
+    }
+
+  {
+    struct timeval currtime;
+
+    gettimeofday (&currtime, NULL);
+    if (currtime.tv_sec > abstime->tv_sec
+        || (currtime.tv_sec == abstime->tv_sec
+            && currtime.tv_usec * 1000 >= abstime->tv_nsec))
+      return ETIMEDOUT;
+
+    EnterCriticalSection (&cond->lock);
+    {
+      struct glwthread_waitqueue_element *elt =
+        glwthread_waitqueue_add (&cond->waiters);
+      LeaveCriticalSection (&cond->lock);
+      if (elt == NULL)
+        {
+          /* Allocation failure.  Weird.  */
+          return EAGAIN;
+        }
+      else
+        {
+          HANDLE event = elt->event;
+          int err;
+          DWORD timeout;
+          DWORD result;
+
+          /* Now release the mutex and let any other thread take it.  */
+          err = mutex_unlock (mutex);
+          if (err != 0)
+            {
+              EnterCriticalSection (&cond->lock);
+              glwthread_waitqueue_remove (&cond->waiters, elt);
+              LeaveCriticalSection (&cond->lock);
+              CloseHandle (event);
+              free (elt);
+              return err;
+            }
+          /* POSIX says:
+              "If another thread is able to acquire the mutex after the
+               about-to-block thread has released it, then a subsequent call to
+               pthread_cond_broadcast() or pthread_cond_signal() in that thread
+               shall behave as if it were issued after the about-to-block thread
+               has blocked."
+             This is fulfilled here, because the thread signalling is done
+             through SetEvent, not PulseEvent.  */
+          /* Wait until another thread signals this event or until the abstime
+             passes.  */
+          gettimeofday (&currtime, NULL);
+          if (currtime.tv_sec > abstime->tv_sec)
+            timeout = 0;
+          else
+            {
+              unsigned long seconds = abstime->tv_sec - currtime.tv_sec;
+              timeout = seconds * 1000;
+              if (timeout / 1000 != seconds) /* overflow? */
+                timeout = INFINITE;
+              else
+                {
+                  long milliseconds =
+                    abstime->tv_nsec / 1000000 - currtime.tv_usec / 1000;
+                  if (milliseconds >= 0)
+                    {
+                      timeout += milliseconds;
+                      if (timeout < milliseconds) /* overflow? */
+                        timeout = INFINITE;
+                    }
+                  else
+                    {
+                      if (timeout >= - milliseconds)
+                        timeout -= (- milliseconds);
+                      else
+                        timeout = 0;
+                    }
+                }
+            }
+          result = WaitForSingleObject (event, timeout);
+          if (result == WAIT_FAILED)
+            abort ();
+          if (result == WAIT_TIMEOUT)
+            {
+              EnterCriticalSection (&cond->lock);
+              if (glwthread_waitqueue_remove (&cond->waiters, elt))
+                {
+                  /* The event was not signaled between the WaitForSingleObject
+                     call and the EnterCriticalSection call.  */
+                  if (!(WaitForSingleObject (event, 0) == WAIT_TIMEOUT))
+                    abort ();
+                }
+              else
+                {
+                  /* The event was signaled between the WaitForSingleObject
+                     call and the EnterCriticalSection call.  */
+                  if (!(WaitForSingleObject (event, 0) == WAIT_OBJECT_0))
+                    abort ();
+                  /* Produce the right return value.  */
+                  result = WAIT_OBJECT_0;
+                }
+              LeaveCriticalSection (&cond->lock);
+            }
+          else
+            {
+              /* The thread which signalled the event already did the
+                 bookkeeping: removed us from the waiters.  */
+            }
+          CloseHandle (event);
+          free (elt);
+          /* Take the mutex again.  It does not matter whether this is done
+             before or after the bookkeeping for WAIT_TIMEOUT.  */
+          err = mutex_lock (mutex);
+          return (err ? err :
+                  result == WAIT_OBJECT_0 ? 0 :
+                  result == WAIT_TIMEOUT ? ETIMEDOUT :
+                  /* WAIT_FAILED shouldn't happen */ EAGAIN);
+        }
+    }
+  }
+}
+
+int
+glwthread_cond_signal (glwthread_cond_t *cond)
+{
+  if (!cond->guard.done)
+    return EINVAL;
+
+  EnterCriticalSection (&cond->lock);
+  /* POSIX says:
+      "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
+       have no effect if there are no threads currently blocked on cond."  */
+  if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
+    glwthread_waitqueue_notify_first (&cond->waiters);
+  LeaveCriticalSection (&cond->lock);
+
+  return 0;
+}
+
+int
+glwthread_cond_broadcast (glwthread_cond_t *cond)
+{
+  if (!cond->guard.done)
+    return EINVAL;
+
+  EnterCriticalSection (&cond->lock);
+  /* POSIX says:
+      "The pthread_cond_broadcast() and pthread_cond_signal() functions shall
+       have no effect if there are no threads currently blocked on cond."
+     glwthread_waitqueue_notify_all is a nop in this case.  */
+  glwthread_waitqueue_notify_all (&cond->waiters);
+  LeaveCriticalSection (&cond->lock);
+
+  return 0;
+}
+
+int
+glwthread_cond_destroy (glwthread_cond_t *cond)
+{
+  if (!cond->guard.done)
+    return EINVAL;
+  if (cond->waiters.wq_list.wql_next != &cond->waiters.wq_list)
+    return EBUSY;
+  DeleteCriticalSection (&cond->lock);
+  cond->guard.done = 0;
+  return 0;
+}
diff --git a/lib/windows-cond.h b/lib/windows-cond.h
new file mode 100644
index 0000000..a21a9d0
--- /dev/null
+++ b/lib/windows-cond.h
@@ -0,0 +1,74 @@
+/* Condition variables (native Windows implementation).
+   Copyright (C) 2008-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008.
+   Based on Bruno Haible <bruno@clisp.org> lock.h */
+
+#ifndef _WINDOWS_COND_H
+#define _WINDOWS_COND_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+#include <time.h>
+
+#include "windows-spinlock.h"
+
+struct glwthread_waitqueue_link
+{
+  struct glwthread_waitqueue_link *wql_next;
+  struct glwthread_waitqueue_link *wql_prev;
+};
+typedef struct
+        {
+          struct glwthread_waitqueue_link wq_list; /* circular list of waiting threads */
+        }
+        glwthread_linked_waitqueue_t;
+typedef struct
+        {
+          glwthread_spinlock_t guard; /* protects the initialization */
+          CRITICAL_SECTION lock; /* protects the remaining fields */
+          glwthread_linked_waitqueue_t waiters; /* waiting threads */
+        }
+        glwthread_cond_t;
+
+#define GLWTHREAD_COND_INIT { GLWTHREAD_SPINLOCK_INIT }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_cond_init (glwthread_cond_t *cond);
+/* Here, to cope with the various types of mutexes, the mutex is a 'void *', and
+   the caller needs to pass the corresponding *_lock and *_unlock functions.  */
+extern int glwthread_cond_wait (glwthread_cond_t *cond,
+                                void *mutex,
+                                int (*mutex_lock) (void *),
+                                int (*mutex_unlock) (void *));
+extern int glwthread_cond_timedwait (glwthread_cond_t *cond,
+                                     void *mutex,
+                                     int (*mutex_lock) (void *),
+                                     int (*mutex_unlock) (void *),
+                                     const struct timespec *abstime);
+extern int glwthread_cond_signal (glwthread_cond_t *cond);
+extern int glwthread_cond_broadcast (glwthread_cond_t *cond);
+extern int glwthread_cond_destroy (glwthread_cond_t *cond);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_COND_H */
diff --git a/modules/cond b/modules/cond
index c3a2dab..82b87e3 100644
--- a/modules/cond
+++ b/modules/cond
@@ -13,7 +13,7 @@ errno
 extern-inline
 stdbool
 time
-gettimeofday
+windows-cond    [test $gl_threads_api = windows]
 
 configure.ac:
 gl_COND
diff --git a/modules/windows-cond b/modules/windows-cond
new file mode 100644
index 0000000..afb70b0
--- /dev/null
+++ b/modules/windows-cond
@@ -0,0 +1,32 @@
+Description:
+Condition variables (native Windows implementation).
+
+Files:
+lib/windows-cond.h
+lib/windows-cond.c
+lib/windows-spinlock.h
+
+Depends-on:
+stdbool
+errno
+time
+gettimeofday
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-cond])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-cond.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #15: 0014-windows-tls-New-module.patch --]
[-- Type: text/x-patch, Size: 6264 bytes --]

From 7435d61f26ef6dece4f94ea00972145b587ef7ab Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:15:30 +0200
Subject: [PATCH 14/26] windows-tls: New module.

* lib/windows-tls.h: New file, based on lib/glthread/tls.h.
* lib/windows-tls.c: New file, based on lib/glthread/tls.h.
* lib/glthread/tls.h: Include windows-tls.h.
(gl_tls_key_t): Define using glwthread_tls_key_t.
* modules/windows-tls: New file.
* modules/tls (Depends-on): Add windows-tls.
---
 ChangeLog           | 10 ++++++++++
 lib/glthread/tls.h  |  4 +++-
 lib/windows-tls.c   | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-tls.h   | 40 ++++++++++++++++++++++++++++++++++++++
 modules/tls         |  1 +
 modules/windows-tls | 27 ++++++++++++++++++++++++++
 6 files changed, 136 insertions(+), 1 deletion(-)
 create mode 100644 lib/windows-tls.c
 create mode 100644 lib/windows-tls.h
 create mode 100644 modules/windows-tls

diff --git a/ChangeLog b/ChangeLog
index 6307fcc..33262df 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-tls: New module.
+	* lib/windows-tls.h: New file, based on lib/glthread/tls.h.
+	* lib/windows-tls.c: New file, based on lib/glthread/tls.h.
+	* lib/glthread/tls.h: Include windows-tls.h.
+	(gl_tls_key_t): Define using glwthread_tls_key_t.
+	* modules/windows-tls: New file.
+	* modules/tls (Depends-on): Add windows-tls.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-cond: New module.
 	* lib/windows-cond.h: New file, based on lib/glthread/cond.h.
 	* lib/windows-cond.c: New file, based on lib/glthread/cond.c.
diff --git a/lib/glthread/tls.h b/lib/glthread/tls.h
index ab85409..7a74234 100644
--- a/lib/glthread/tls.h
+++ b/lib/glthread/tls.h
@@ -236,9 +236,11 @@ extern void *glthread_tls_get_multithreaded (thread_key_t key);
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 # include <windows.h>
 
+# include "windows-tls.h"
+
 /* ------------------------- gl_tls_key_t datatype ------------------------- */
 
-typedef DWORD gl_tls_key_t;
+typedef glwthread_tls_key_t gl_tls_key_t;
 # define glthread_tls_key_init(KEY, DESTRUCTOR) \
     /* The destructor is unsupported.  */    \
     ((*(KEY) = TlsAlloc ()) == (DWORD)-1 ? EAGAIN : ((void) (DESTRUCTOR), 0))
diff --git a/lib/windows-tls.c b/lib/windows-tls.c
new file mode 100644
index 0000000..87aa454
--- /dev/null
+++ b/lib/windows-tls.c
@@ -0,0 +1,55 @@
+/* Thread-local storage (native Windows implementation).
+   Copyright (C) 2005-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-tls.h"
+
+int
+glwthread_tls_key_create (glwthread_tls_key_t *keyp, void (*destructor) (void *))
+{
+  /* TODO: The destructor is unsupported.  */
+  (void) destructor;
+
+  if ((*keyp = TlsAlloc ()) == (DWORD)-1)
+    return EAGAIN;
+  return 0;
+}
+
+void *
+glwthread_tls_get (glwthread_tls_key_t key)
+{
+  return TlsGetValue (key);
+}
+
+int
+glwthread_tls_set (glwthread_tls_key_t key, void *value)
+{
+  if (!TlsSetValue (key, value))
+    return EINVAL;
+  return 0;
+}
+
+int
+glwthread_tls_key_delete (glwthread_tls_key_t key)
+{
+  if (!TlsFree (key))
+    return EINVAL;
+  return 0;
+}
diff --git a/lib/windows-tls.h b/lib/windows-tls.h
new file mode 100644
index 0000000..5e79545
--- /dev/null
+++ b/lib/windows-tls.h
@@ -0,0 +1,40 @@
+/* Thread-local storage (native Windows implementation).
+   Copyright (C) 2005-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#ifndef _WINDOWS_TLS_H
+#define _WINDOWS_TLS_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+typedef DWORD glwthread_tls_key_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_tls_key_create (glwthread_tls_key_t *keyp, void (*destructor) (void *));
+extern void *glwthread_tls_get (glwthread_tls_key_t key);
+extern int glwthread_tls_set (glwthread_tls_key_t key, void *value);
+extern int glwthread_tls_key_delete (glwthread_tls_key_t key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_TLS_H */
diff --git a/modules/tls b/modules/tls
index 5d61044..f4a5256 100644
--- a/modules/tls
+++ b/modules/tls
@@ -8,6 +8,7 @@ m4/tls.m4
 
 Depends-on:
 threadlib
+windows-tls     [test $gl_threads_api = windows]
 
 configure.ac:
 gl_TLS
diff --git a/modules/windows-tls b/modules/windows-tls
new file mode 100644
index 0000000..32a32eb
--- /dev/null
+++ b/modules/windows-tls
@@ -0,0 +1,27 @@
+Description:
+Thread-local storage (native Windows implementation).
+
+Files:
+lib/windows-tls.h
+lib/windows-tls.c
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-tls])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-tls.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #16: 0015-windows-thread-New-module.patch --]
[-- Type: text/x-patch, Size: 20481 bytes --]

From 0894f96f89e0e44bdf2921e6cd51fca429bf9802 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:16:20 +0200
Subject: [PATCH 15/26] windows-thread: New module.

* lib/windows-thread.h: New file, based on lib/glthread/thread.h.
* lib/windows-thread.c: New file, based on lib/glthread/thread.c.
* lib/glthread/thread.h: Include windows-thread.h.
(gl_thread_t): Define using glwthread_thread_t.
(glthread_create): Define using glwthread_thread_create.
(glthread_join): Define using glwthread_thread_join.
(gl_thread_self): Define using glwthread_thread_self.
(gl_thread_exit): Define using glwthread_thread_exit.
(glthread_create_func, glthread_join_func, gl_thread_self_func,
gl_thread_exit_func): Remove declarations.
* lib/glthread/thread.c (self_key): Remove variable.
(do_init_self_key, init_self_key): Remove functions.
(struct gl_thread_struct): Remove type.
(get_current_thread_handle, gl_thread_self_func, wrapper_func,
glthread_create_func, glthread_join_func, gl_thread_exit_func): Remove
functions.
* modules/windows-thread: New file.
* modules/thread (Depends-on): Add windows-thread.
---
 ChangeLog              |  22 +++++
 lib/glthread/thread.c  | 182 -------------------------------------
 lib/glthread/thread.h  |  25 ++----
 lib/windows-thread.c   | 237 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/windows-thread.h   |  52 +++++++++++
 modules/thread         |   1 +
 modules/windows-thread |  28 ++++++
 7 files changed, 347 insertions(+), 200 deletions(-)
 create mode 100644 lib/windows-thread.c
 create mode 100644 lib/windows-thread.h
 create mode 100644 modules/windows-thread

diff --git a/ChangeLog b/ChangeLog
index 33262df..ab97f37 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	windows-thread: New module.
+	* lib/windows-thread.h: New file, based on lib/glthread/thread.h.
+	* lib/windows-thread.c: New file, based on lib/glthread/thread.c.
+	* lib/glthread/thread.h: Include windows-thread.h.
+	(gl_thread_t): Define using glwthread_thread_t.
+	(glthread_create): Define using glwthread_thread_create.
+	(glthread_join): Define using glwthread_thread_join.
+	(gl_thread_self): Define using glwthread_thread_self.
+	(gl_thread_exit): Define using glwthread_thread_exit.
+	(glthread_create_func, glthread_join_func, gl_thread_self_func,
+	gl_thread_exit_func): Remove declarations.
+	* lib/glthread/thread.c (self_key): Remove variable.
+	(do_init_self_key, init_self_key): Remove functions.
+	(struct gl_thread_struct): Remove type.
+	(get_current_thread_handle, gl_thread_self_func, wrapper_func,
+	glthread_create_func, glthread_join_func, gl_thread_exit_func): Remove
+	functions.
+	* modules/windows-thread: New file.
+	* modules/thread (Depends-on): Add windows-thread.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-tls: New module.
 	* lib/windows-tls.h: New file, based on lib/glthread/tls.h.
 	* lib/windows-tls.c: New file, based on lib/glthread/tls.h.
diff --git a/lib/glthread/thread.c b/lib/glthread/thread.c
index 9461949..9da0542 100644
--- a/lib/glthread/thread.c
+++ b/lib/glthread/thread.c
@@ -45,188 +45,6 @@ const gl_thread_t gl_null_thread /* = { .p = NULL } */;
 
 #if USE_WINDOWS_THREADS
 
-#include <process.h>
-
-/* -------------------------- gl_thread_t datatype -------------------------- */
-
-/* The Thread-Local Storage (TLS) key that allows to access each thread's
-   'struct gl_thread_struct *' pointer.  */
-static DWORD self_key = (DWORD)-1;
-
-/* Initializes self_key.  This function must only be called once.  */
-static void
-do_init_self_key (void)
-{
-  self_key = TlsAlloc ();
-  /* If this fails, we're hosed.  */
-  if (self_key == (DWORD)-1)
-    abort ();
-}
-
-/* Initializes self_key.  */
-static void
-init_self_key (void)
-{
-  gl_once_define(static, once)
-  gl_once (once, do_init_self_key);
-}
-
-/* This structure contains information about a thread.
-   It is stored in TLS under key self_key.  */
-struct gl_thread_struct
-{
-  /* Fields for managing the handle.  */
-  HANDLE volatile handle;
-  CRITICAL_SECTION handle_lock;
-  /* Fields for managing the exit value.  */
-  void * volatile result;
-  /* Fields for managing the thread start.  */
-  void * (*func) (void *);
-  void *arg;
-};
-
-/* Return a real HANDLE object for the current thread.  */
-static HANDLE
-get_current_thread_handle (void)
-{
-  HANDLE this_handle;
-
-  /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
-     identifier, not a real handle.  */
-  if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
-                        GetCurrentProcess (), &this_handle,
-                        0, FALSE, DUPLICATE_SAME_ACCESS))
-    abort ();
-  return this_handle;
-}
-
-gl_thread_t
-gl_thread_self_func (void)
-{
-  gl_thread_t thread;
-
-  if (self_key == (DWORD)-1)
-    init_self_key ();
-  thread = TlsGetValue (self_key);
-  if (thread == NULL)
-    {
-      /* This happens only in threads that have not been created through
-         glthread_create(), such as the main thread.  */
-      for (;;)
-        {
-          thread =
-            (struct gl_thread_struct *)
-            malloc (sizeof (struct gl_thread_struct));
-          if (thread != NULL)
-            break;
-          /* Memory allocation failed.  There is not much we can do.  Have to
-             busy-loop, waiting for the availability of memory.  */
-          Sleep (1);
-        }
-
-      thread->handle = get_current_thread_handle ();
-      InitializeCriticalSection (&thread->handle_lock);
-      thread->result = NULL; /* just to be deterministic */
-      TlsSetValue (self_key, thread);
-    }
-  return thread;
-}
-
-/* The main function of a freshly creating thread.  It's a wrapper around
-   the FUNC and ARG arguments passed to glthread_create_func.  */
-static unsigned int WINAPI
-wrapper_func (void *varg)
-{
-  struct gl_thread_struct *thread = (struct gl_thread_struct *)varg;
-
-  EnterCriticalSection (&thread->handle_lock);
-  /* Create a new handle for the thread only if the parent thread did not yet
-     fill in the handle.  */
-  if (thread->handle == NULL)
-    thread->handle = get_current_thread_handle ();
-  LeaveCriticalSection (&thread->handle_lock);
-
-  if (self_key == (DWORD)-1)
-    init_self_key ();
-  TlsSetValue (self_key, thread);
-
-  /* Run the thread.  Store the exit value if the thread was not terminated
-     otherwise.  */
-  thread->result = thread->func (thread->arg);
-  return 0;
-}
-
-int
-glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg)
-{
-  struct gl_thread_struct *thread =
-    (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct));
-  if (thread == NULL)
-    return ENOMEM;
-  thread->handle = NULL;
-  InitializeCriticalSection (&thread->handle_lock);
-  thread->result = NULL; /* just to be deterministic */
-  thread->func = func;
-  thread->arg = arg;
-
-  {
-    unsigned int thread_id;
-    HANDLE thread_handle;
-
-    thread_handle = (HANDLE)
-      _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
-      /* calls CreateThread with the same arguments */
-    if (thread_handle == NULL)
-      {
-        DeleteCriticalSection (&thread->handle_lock);
-        free (thread);
-        return EAGAIN;
-      }
-
-    EnterCriticalSection (&thread->handle_lock);
-    if (thread->handle == NULL)
-      thread->handle = thread_handle;
-    else
-      /* thread->handle was already set by the thread itself.  */
-      CloseHandle (thread_handle);
-    LeaveCriticalSection (&thread->handle_lock);
-
-    *threadp = thread;
-    return 0;
-  }
-}
-
-int
-glthread_join_func (gl_thread_t thread, void **retvalp)
-{
-  if (thread == NULL)
-    return EINVAL;
-
-  if (thread == gl_thread_self ())
-    return EDEADLK;
-
-  if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
-    return EINVAL;
-
-  if (retvalp != NULL)
-    *retvalp = thread->result;
-
-  DeleteCriticalSection (&thread->handle_lock);
-  CloseHandle (thread->handle);
-  free (thread);
-
-  return 0;
-}
-
-int
-gl_thread_exit_func (void *retval)
-{
-  gl_thread_t thread = gl_thread_self ();
-  thread->result = retval;
-  _endthreadex (0); /* calls ExitThread (0) */
-  abort ();
-}
-
 #endif
 
 /* ========================================================================= */
diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h
index f263129..bf4e74a 100644
--- a/lib/glthread/thread.h
+++ b/lib/glthread/thread.h
@@ -336,39 +336,28 @@ typedef thread_t gl_thread_t;
 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
 # include <windows.h>
 
+# include "windows-thread.h"
+
 # ifdef __cplusplus
 extern "C" {
 # endif
 
 /* -------------------------- gl_thread_t datatype -------------------------- */
 
-/* The gl_thread_t is a pointer to a structure in memory.
-   Why not the thread handle?  If it were the thread handle, it would be hard
-   to implement gl_thread_self() (since GetCurrentThread () returns a pseudo-
-   handle, DuplicateHandle (GetCurrentThread ()) returns a handle that must be
-   closed afterwards, and there is no function for quickly retrieving a thread
-   handle from its id).
-   Why not the thread id?  I tried it.  It did not work: Sometimes ids appeared
-   that did not belong to running threads, and glthread_join failed with ESRCH.
- */
-typedef struct gl_thread_struct *gl_thread_t;
+typedef glwthread_thread_t gl_thread_t;
 # define glthread_create(THREADP, FUNC, ARG) \
-    glthread_create_func (THREADP, FUNC, ARG)
+    glwthread_thread_create (THREADP, FUNC, ARG)
 # define glthread_sigmask(HOW, SET, OSET) \
     /* unsupported */ 0
 # define glthread_join(THREAD, RETVALP) \
-    glthread_join_func (THREAD, RETVALP)
+    glwthread_thread_join (THREAD, RETVALP)
 # define gl_thread_self() \
-    gl_thread_self_func ()
+    glwthread_thread_self ()
 # define gl_thread_self_pointer() \
     gl_thread_self ()
 # define gl_thread_exit(RETVAL) \
-    gl_thread_exit_func (RETVAL)
+    glwthread_thread_exit (RETVAL)
 # define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0
-extern int glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg);
-extern int glthread_join_func (gl_thread_t thread, void **retvalp);
-extern gl_thread_t gl_thread_self_func (void);
-extern int gl_thread_exit_func (void *retval);
 
 # ifdef __cplusplus
 }
diff --git a/lib/windows-thread.c b/lib/windows-thread.c
new file mode 100644
index 0000000..01daa78
--- /dev/null
+++ b/lib/windows-thread.c
@@ -0,0 +1,237 @@
+/* Creating and controlling threads (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "windows-thread.h"
+
+#include <process.h>
+#include <stdlib.h>
+
+#include "windows-once.h"
+
+/* The Thread-Local Storage (TLS) key that allows to access each thread's
+   'struct glwthread_thread_struct *' pointer.  */
+static DWORD self_key = (DWORD)-1;
+
+/* Initializes self_key.  This function must only be called once.  */
+static void
+do_init_self_key (void)
+{
+  self_key = TlsAlloc ();
+  /* If this fails, we're hosed.  */
+  if (self_key == (DWORD)-1)
+    abort ();
+}
+
+/* Initializes self_key.  */
+static void
+init_self_key (void)
+{
+  static glwthread_once_t once = GLWTHREAD_ONCE_INIT;
+  glwthread_once (&once, do_init_self_key);
+}
+
+/* This structure contains information about a thread.
+   It is stored in TLS under key self_key.  */
+struct glwthread_thread_struct
+{
+  /* Fields for managing the handle.  */
+  HANDLE volatile handle;
+  CRITICAL_SECTION handle_lock;
+  /* Fields for managing the exit value.  */
+  BOOL volatile detached;
+  void * volatile result;
+  /* Fields for managing the thread start.  */
+  void * (*func) (void *);
+  void *arg;
+};
+
+/* Return a real HANDLE object for the current thread.  */
+static HANDLE
+get_current_thread_handle (void)
+{
+  HANDLE this_handle;
+
+  /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic
+     identifier, not a real handle.  */
+  if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+                        GetCurrentProcess (), &this_handle,
+                        0, FALSE, DUPLICATE_SAME_ACCESS))
+    abort ();
+  return this_handle;
+}
+
+glwthread_thread_t
+glwthread_thread_self (void)
+{
+  glwthread_thread_t thread;
+
+  if (self_key == (DWORD)-1)
+    init_self_key ();
+  thread = TlsGetValue (self_key);
+  if (thread == NULL)
+    {
+      /* This happens only in threads that have not been created through
+         glthread_create(), such as the main thread.  */
+      for (;;)
+        {
+          thread =
+            (struct glwthread_thread_struct *)
+            malloc (sizeof (struct glwthread_thread_struct));
+          if (thread != NULL)
+            break;
+          /* Memory allocation failed.  There is not much we can do.  Have to
+             busy-loop, waiting for the availability of memory.  */
+          Sleep (1);
+        }
+
+      thread->handle = get_current_thread_handle ();
+      InitializeCriticalSection (&thread->handle_lock);
+      thread->detached = FALSE; /* This can lead to a memory leak.  */
+      thread->result = NULL; /* just to be deterministic */
+      TlsSetValue (self_key, thread);
+    }
+  return thread;
+}
+
+/* The main function of a freshly creating thread.  It's a wrapper around
+   the FUNC and ARG arguments passed to glthread_create_func.  */
+static unsigned int WINAPI
+wrapper_func (void *varg)
+{
+  struct glwthread_thread_struct *thread =
+    (struct glwthread_thread_struct *) varg;
+
+  EnterCriticalSection (&thread->handle_lock);
+  /* Create a new handle for the thread only if the parent thread did not yet
+     fill in the handle.  */
+  if (thread->handle == NULL)
+    thread->handle = get_current_thread_handle ();
+  LeaveCriticalSection (&thread->handle_lock);
+
+  if (self_key == (DWORD)-1)
+    init_self_key ();
+  TlsSetValue (self_key, thread);
+
+  /* Run the thread.  Store the exit value if the thread was not terminated
+     otherwise.  */
+  thread->result = thread->func (thread->arg);
+
+  if (thread->detached)
+    {
+      /* Clean up the thread, like thrd_join would do.  */
+      DeleteCriticalSection (&thread->handle_lock);
+      CloseHandle (thread->handle);
+      free (thread);
+    }
+
+  return 0;
+}
+
+int
+glwthread_thread_create (glwthread_thread_t *threadp,
+                         void * (*func) (void *), void *arg)
+{
+  struct glwthread_thread_struct *thread =
+    (struct glwthread_thread_struct *)
+    malloc (sizeof (struct glwthread_thread_struct));
+  if (thread == NULL)
+    return ENOMEM;
+  thread->handle = NULL;
+  InitializeCriticalSection (&thread->handle_lock);
+  thread->detached = FALSE;
+  thread->result = NULL; /* just to be deterministic */
+  thread->func = func;
+  thread->arg = arg;
+
+  {
+    unsigned int thread_id;
+    HANDLE thread_handle;
+
+    thread_handle = (HANDLE)
+      _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
+      /* calls CreateThread with the same arguments */
+    if (thread_handle == NULL)
+      {
+        DeleteCriticalSection (&thread->handle_lock);
+        free (thread);
+        return EAGAIN;
+      }
+
+    EnterCriticalSection (&thread->handle_lock);
+    if (thread->handle == NULL)
+      thread->handle = thread_handle;
+    else
+      /* thread->handle was already set by the thread itself.  */
+      CloseHandle (thread_handle);
+    LeaveCriticalSection (&thread->handle_lock);
+
+    *threadp = thread;
+    return 0;
+  }
+}
+
+int
+glwthread_thread_join (glwthread_thread_t thread, void **retvalp)
+{
+  if (thread == NULL)
+    return EINVAL;
+
+  if (thread == glwthread_thread_self ())
+    return EDEADLK;
+
+  if (thread->detached)
+    return EINVAL;
+
+  if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED)
+    return EINVAL;
+
+  if (retvalp != NULL)
+    *retvalp = thread->result;
+
+  DeleteCriticalSection (&thread->handle_lock);
+  CloseHandle (thread->handle);
+  free (thread);
+
+  return 0;
+}
+
+int
+glwthread_thread_detach (glwthread_thread_t thread)
+{
+  if (thread == NULL)
+    return EINVAL;
+
+  if (thread->detached)
+    return EINVAL;
+
+  thread->detached = TRUE;
+  return 0;
+}
+
+int
+glwthread_thread_exit (void *retval)
+{
+  glwthread_thread_t thread = glwthread_thread_self ();
+  thread->result = retval;
+  _endthreadex (0); /* calls ExitThread (0) */
+  abort ();
+}
diff --git a/lib/windows-thread.h b/lib/windows-thread.h
new file mode 100644
index 0000000..8bf98fb
--- /dev/null
+++ b/lib/windows-thread.h
@@ -0,0 +1,52 @@
+/* Creating and controlling threads (native Windows implementation).
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-win32.h.  */
+
+#ifndef _WINDOWS_THREAD_H
+#define _WINDOWS_THREAD_H
+
+#define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#include <windows.h>
+
+/* The glwthread_thread_t is a pointer to a structure in memory.
+   Why not the thread handle?  If it were the thread handle, it would be hard
+   to implement glwthread_thread_self() (since GetCurrentThread () returns a
+   pseudo-handle, DuplicateHandle (GetCurrentThread ()) returns a handle that
+   must be closed afterwards, and there is no function for quickly retrieving
+   a thread handle from its id).
+   Why not the thread id?  I tried it.  It did not work: Sometimes ids appeared
+   that did not belong to running threads, and glthread_join failed with ESRCH.
+ */
+typedef struct glwthread_thread_struct *glwthread_thread_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int glwthread_thread_create (glwthread_thread_t *threadp,
+                                    void * (*func) (void *), void *arg);
+extern int glwthread_thread_join (glwthread_thread_t thread, void **retvalp);
+extern int glwthread_thread_detach (glwthread_thread_t thread);
+extern glwthread_thread_t glwthread_thread_self (void);
+extern int glwthread_thread_exit (void *retval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WINDOWS_THREAD_H */
diff --git a/modules/thread b/modules/thread
index b4e9d78..baa8887 100644
--- a/modules/thread
+++ b/modules/thread
@@ -10,6 +10,7 @@ Depends-on:
 threadlib
 extern-inline
 lock
+windows-thread  [test $gl_threads_api = windows]
 pthread_sigmask [test $gl_threads_api = posix]
 
 configure.ac:
diff --git a/modules/windows-thread b/modules/windows-thread
new file mode 100644
index 0000000..3f35344
--- /dev/null
+++ b/modules/windows-thread
@@ -0,0 +1,28 @@
+Description:
+Creating and controlling threads (native Windows implementation).
+
+Files:
+lib/windows-thread.h
+lib/windows-thread.c
+
+Depends-on:
+windows-once
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+case "$host_os" in
+  mingw*)
+    AC_LIBOBJ([windows-thread])
+    ;;
+esac
+
+Makefile.am:
+
+Include:
+"windows-thread.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH] ISO C 11 threads implementation
  2019-06-20 10:40 ISO C 11 threads Bruno Haible
  2019-06-20 10:42 ` [PATCH] ISO C 11 threads preparations Bruno Haible
@ 2019-06-20 10:45 ` Bruno Haible
  2019-06-20 16:34   ` Paul Eggert
                     ` (2 more replies)
  2019-06-20 17:19 ` ISO C 11 threads Bruno Haible
  2019-06-21  1:12 ` Bruno Haible
  3 siblings, 3 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-20 10:45 UTC (permalink / raw)
  To: bug-gnulib

[-- Attachment #1: Type: text/plain, Size: 759 bytes --]

These are the patches that provide the ISO C 11 <threads.h> (except for
'thread_local').

The implementation is based on <pthread.h> for POSIX threads and on the
windows-* modules for native Windows. The only major headache was how to
work around the terrible AIX bugs.

The unit tests are adapted from the unit tests of 'thread', 'lock', 'cond',
'tls'.

16  threads-h: New module.
17  threads-h: Add tests.

This is just the header file.

18  thrd: New module.
19  mtx: New module.
20  cnd: New module.
21  tss: New module.
22  thrd: Add tests.
23  mtx: Add tests.
24  cnd: Add tests.
25  tss: Add tests.

These are the function implementations.

26  threads: New module.

This is just a convenience module that requests all of the function
implementations.

[-- Attachment #2: 0016-threads-h-New-module.patch --]
[-- Type: text/x-patch, Size: 28807 bytes --]

From 80d24481060742c00cfe83a6a772db3856d1e7cb Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:17:30 +0200
Subject: [PATCH 16/26] threads-h: New module.

* lib/threads.in.h: New file.
* m4/threads.m4: New file.
* m4/yield.m4 (gl_YIELD): Update comment.
* modules/threads-h: New file.
* modules/yields (configure.ac): Use AC_REQUIRE.
* doc/posix-headers/threads.texi: Mention the new module and the AIX
bugs.
---
 ChangeLog                      |  11 +
 doc/posix-headers/threads.texi |  14 +-
 lib/threads.in.h               | 649 +++++++++++++++++++++++++++++++++++++++++
 m4/threads.m4                  | 126 ++++++++
 m4/yield.m4                    |   4 +-
 modules/threads-h              |  73 +++++
 modules/yield                  |   2 +-
 7 files changed, 872 insertions(+), 7 deletions(-)
 create mode 100644 lib/threads.in.h
 create mode 100644 m4/threads.m4
 create mode 100644 modules/threads-h

diff --git a/ChangeLog b/ChangeLog
index ab97f37..22b942b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	threads-h: New module.
+	* lib/threads.in.h: New file.
+	* m4/threads.m4: New file.
+	* m4/yield.m4 (gl_YIELD): Update comment.
+	* modules/threads-h: New file.
+	* modules/yields (configure.ac): Use AC_REQUIRE.
+	* doc/posix-headers/threads.texi: Mention the new module and the AIX
+	bugs.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	windows-thread: New module.
 	* lib/windows-thread.h: New file, based on lib/glthread/thread.h.
 	* lib/windows-thread.c: New file, based on lib/glthread/thread.c.
diff --git a/doc/posix-headers/threads.texi b/doc/posix-headers/threads.texi
index 9892539..71ae43a 100644
--- a/doc/posix-headers/threads.texi
+++ b/doc/posix-headers/threads.texi
@@ -3,15 +3,21 @@
 
 Defines the multithreading facility of ISO C11.
 
-Gnulib module: ---
+Gnulib module: threads-h
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This header file is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
+@item
+This header file defines @code{thrd_start_t} incorrectly on some platforms:
+AIX 7.2.
+@item
+This header file does not define @code{TSS_DTOR_ITERATIONS} on some platforms:
+AIX 7.2.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This header file is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/lib/threads.in.h b/lib/threads.in.h
new file mode 100644
index 0000000..a18d64b
--- /dev/null
+++ b/lib/threads.in.h
@@ -0,0 +1,649 @@
+/* An ISO C 11 compatible <threads.h>.
+
+   Copyright (C) 2019 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, see <https://www.gnu.org/licenses/>.  */
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#ifndef _@GUARD_PREFIX@_THREADS_H
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_THREADS_H@
+# @INCLUDE_NEXT@ @NEXT_THREADS_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_THREADS_H
+#define _@GUARD_PREFIX@_THREADS_H
+
+#if !@HAVE_THREADS_H@
+
+# include <time.h>
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#  include <windows.h>
+
+# else
+/* Use POSIX threads.  */
+
+#  include <pthread.h>
+
+# endif
+
+#endif
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here.  */
+
+/* The definition of _Noreturn is copied here.  */
+
+/* The definition of _GL_ARG_NONNULL is copied here.  */
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* Storage class specifier for thread-local storage.  */
+#ifdef _AIX
+/* The macro definition from AIX 7.1..7.2 <threads.h> is unusable, because
+   its expansion ends in a semicolon.  */
+# undef thread_local
+#endif
+#if !@HAVE_THREADS_H@ || !defined thread_local
+# define thread_local _Thread_local
+#endif
+
+
+/* =========== ISO C 11 7.26.5 Thread functions =========== */
+
+#if !@HAVE_THREADS_H@
+
+/* Return codes.  */
+enum
+{
+  thrd_success  = 0,
+  thrd_timedout = 1,
+  thrd_busy     = 2,
+  thrd_nomem    = 3,
+  thrd_error    = 4
+};
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  include "windows-thread.h"
+
+typedef glwthread_thread_t thrd_t;
+
+# else
+/* Use POSIX threads.  */
+
+typedef pthread_t thrd_t;
+
+# endif
+
+#endif
+
+#if @BROKEN_THRD_START_T@
+/* Need to override thrd_start_t, to make thrd_create work.  */
+# define thrd_start_t rpl_thrd_start_t
+/* Need to override thrd_t, to make thrd_join work.  */
+struct thrd_with_exitcode
+{
+  thrd_t volatile tid;
+  int volatile detached;
+  int volatile exitcode;
+};
+typedef struct thrd_with_exitcode *rpl_thrd_t;
+# define thrd_t rpl_thrd_t
+#endif
+/* Type of the main function of a thread.  */
+#if !@HAVE_THREADS_H@ || @BROKEN_THRD_START_T@
+typedef int (* thrd_start_t) (void *);
+#endif
+
+#if @GNULIB_THRD@
+# if @REPLACE_THRD_CREATE@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define thrd_create rpl_thrd_create
+#  endif
+_GL_FUNCDECL_RPL (thrd_create, int, (thrd_t *, thrd_start_t, void *)
+                                    _GL_ARG_NONNULL ((1, 2)));
+_GL_CXXALIAS_RPL (thrd_create, int, (thrd_t *, thrd_start_t, void *));
+# else
+#  if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_create, int, (thrd_t *, thrd_start_t, void *)
+                                    _GL_ARG_NONNULL ((1, 2)));
+#  endif
+_GL_CXXALIAS_SYS (thrd_create, int, (thrd_t *, thrd_start_t, void *));
+# endif
+_GL_CXXALIASWARN (thrd_create);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_create
+# if HAVE_RAW_DECL_THRD_CREATE
+_GL_WARN_ON_USE (thrd_create, "thrd_create is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if @REPLACE_THRD_CURRENT@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define thrd_current rpl_thrd_current
+#  endif
+_GL_FUNCDECL_RPL (thrd_current, thrd_t, (void) _GL_ATTRIBUTE_PURE);
+_GL_CXXALIAS_RPL (thrd_current, thrd_t, (void));
+# else
+#  if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_current, thrd_t, (void) _GL_ATTRIBUTE_PURE);
+#  endif
+_GL_CXXALIAS_SYS (thrd_current, thrd_t, (void));
+# endif
+_GL_CXXALIASWARN (thrd_current);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_current
+# if HAVE_RAW_DECL_THRD_CURRENT
+_GL_WARN_ON_USE (thrd_current, "thrd_current is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if @REPLACE_THRD_EQUAL@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define thrd_equal rpl_thrd_equal
+#  endif
+_GL_FUNCDECL_RPL (thrd_equal, int, (thrd_t, thrd_t) _GL_ATTRIBUTE_PURE);
+_GL_CXXALIAS_RPL (thrd_equal, int, (thrd_t, thrd_t));
+# else
+#  if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_equal, int, (thrd_t, thrd_t) _GL_ATTRIBUTE_PURE);
+#  endif
+_GL_CXXALIAS_SYS (thrd_equal, int, (thrd_t, thrd_t));
+# endif
+_GL_CXXALIASWARN (thrd_equal);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_equal
+# if HAVE_RAW_DECL_THRD_EQUAL
+_GL_WARN_ON_USE (thrd_equal, "thrd_equal is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_sleep, int,
+                              (const struct timespec *, struct timespec *)
+                              _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (thrd_sleep, int,
+                              (const struct timespec *, struct timespec *));
+_GL_CXXALIASWARN (thrd_sleep);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_sleep
+# if HAVE_RAW_DECL_THRD_SLEEP
+_GL_WARN_ON_USE (thrd_sleep, "thrd_sleep is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_yield, void, (void));
+# endif
+_GL_CXXALIAS_SYS (thrd_yield, void, (void));
+_GL_CXXALIASWARN (thrd_yield);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_yield
+# if HAVE_RAW_DECL_THRD_YIELD
+_GL_WARN_ON_USE (thrd_yield, "thrd_yield is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if @REPLACE_THRD_DETACH@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define thrd_detach rpl_thrd_detach
+#  endif
+_GL_FUNCDECL_RPL (thrd_detach, int, (thrd_t));
+_GL_CXXALIAS_RPL (thrd_detach, int, (thrd_t));
+# else
+#  if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_detach, int, (thrd_t));
+#  endif
+_GL_CXXALIAS_SYS (thrd_detach, int, (thrd_t));
+# endif
+_GL_CXXALIASWARN (thrd_detach);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_detach
+# if HAVE_RAW_DECL_THRD_DETACH
+_GL_WARN_ON_USE (thrd_detach, "thrd_detach is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if @REPLACE_THRD_JOIN@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define thrd_join rpl_thrd_join
+#  endif
+_GL_FUNCDECL_RPL (thrd_join, int, (thrd_t, int *));
+_GL_CXXALIAS_RPL (thrd_join, int, (thrd_t, int *));
+# else
+#  if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_join, int, (thrd_t, int *));
+#  endif
+_GL_CXXALIAS_SYS (thrd_join, int, (thrd_t, int *));
+# endif
+_GL_CXXALIASWARN (thrd_join);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_join
+# if HAVE_RAW_DECL_THRD_JOIN
+_GL_WARN_ON_USE (thrd_join, "thrd_join is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+#if @GNULIB_THRD@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (thrd_exit, _Noreturn void, (int));
+# endif
+_GL_CXXALIAS_SYS (thrd_exit, _Noreturn void, (int));
+_GL_CXXALIASWARN (thrd_exit);
+#elif defined GNULIB_POSIXCHECK
+# undef thrd_exit
+# if HAVE_RAW_DECL_THRD_EXIT
+_GL_WARN_ON_USE (thrd_exit, "thrd_exit is unportable - "
+                 "use gnulib module thrd for portability");
+# endif
+#endif
+
+
+/* =========== ISO C 11 7.26.4 Mutex functions =========== */
+
+#if !@HAVE_THREADS_H@
+
+/* Types of mutexes.  */
+enum
+{
+  mtx_plain     = 0,
+  mtx_timed     = 1,
+  mtx_recursive = 2
+};
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  include "windows-mutex.h"
+#  include "windows-recmutex.h"
+#  include "windows-timedmutex.h"
+#  include "windows-timedrecmutex.h"
+
+typedef struct
+        {
+          int type;
+          union
+            {
+              glwthread_mutex_t         u_mutex;
+              glwthread_recmutex_t      u_recmutex;
+              glwthread_timedmutex_t    u_timedmutex;
+              glwthread_timedrecmutex_t u_timedrecmutex;
+            }
+          u;
+        }
+        mtx_t;
+
+# else
+/* Use POSIX threads.  */
+
+typedef pthread_mutex_t mtx_t;
+
+# endif
+
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_init, int, (mtx_t *, int) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (mtx_init, int, (mtx_t *, int));
+_GL_CXXALIASWARN (mtx_init);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_init
+# if HAVE_RAW_DECL_MTX_INIT
+_GL_WARN_ON_USE (mtx_init, "mtx_init is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_lock, int, (mtx_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (mtx_lock, int, (mtx_t *));
+_GL_CXXALIASWARN (mtx_lock);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_lock
+# if HAVE_RAW_DECL_MTX_LOCK
+_GL_WARN_ON_USE (mtx_lock, "mtx_lock is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_trylock, int, (mtx_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (mtx_trylock, int, (mtx_t *));
+_GL_CXXALIASWARN (mtx_trylock);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_trylock
+# if HAVE_RAW_DECL_MTX_TRYLOCK
+_GL_WARN_ON_USE (mtx_trylock, "mtx_trylock is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_timedlock, int, (mtx_t *, const struct timespec *)
+                                      _GL_ARG_NONNULL ((1, 2)));
+# endif
+_GL_CXXALIAS_SYS (mtx_timedlock, int, (mtx_t *, const struct timespec *));
+_GL_CXXALIASWARN (mtx_timedlock);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_timedlock
+# if HAVE_RAW_DECL_MTX_TIMEDLOCK
+_GL_WARN_ON_USE (mtx_timedlock, "mtx_timedlock is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_unlock, int, (mtx_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (mtx_unlock, int, (mtx_t *));
+_GL_CXXALIASWARN (mtx_unlock);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_unlock
+# if HAVE_RAW_DECL_MTX_UNLOCK
+_GL_WARN_ON_USE (mtx_unlock, "mtx_unlock is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (mtx_destroy, void, (mtx_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (mtx_destroy, void, (mtx_t *));
+_GL_CXXALIASWARN (mtx_destroy);
+#elif defined GNULIB_POSIXCHECK
+# undef mtx_destroy
+# if HAVE_RAW_DECL_MTX_DESTROY
+_GL_WARN_ON_USE (mtx_destroy, "mtx_destroy is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+
+/* =========== ISO C 11 7.26.2 Initialization functions =========== */
+
+#if !@HAVE_THREADS_H@
+
+/* Type that contains a flag for use by call_once.  */
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  include "windows-once.h"
+
+typedef glwthread_once_t once_flag;
+#  define ONCE_FLAG_INIT GLWTHREAD_ONCE_INIT
+
+# else
+/* Use POSIX threads.  */
+
+typedef pthread_once_t once_flag;
+#  define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
+
+# endif
+
+#endif
+
+#if @GNULIB_MTX@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (call_once, void, (once_flag *, void (*) (void))
+                                   _GL_ARG_NONNULL ((1, 2)));
+# endif
+_GL_CXXALIAS_SYS (call_once, void, (once_flag *, void (*) (void)));
+_GL_CXXALIASWARN (call_once);
+#elif defined GNULIB_POSIXCHECK
+# undef call_once
+# if HAVE_RAW_DECL_CALL_ONCE
+_GL_WARN_ON_USE (call_once, "call_once is unportable - "
+                 "use gnulib module mtx for portability");
+# endif
+#endif
+
+
+/* =========== ISO C 11 7.26.3 Condition variable functions =========== */
+
+#if !@HAVE_THREADS_H@
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  include "windows-cond.h"
+
+typedef glwthread_cond_t cnd_t;
+
+# else
+/* Use POSIX threads.  */
+
+typedef pthread_cond_t cnd_t;
+
+# endif
+
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_init, int, (cnd_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (cnd_init, int, (cnd_t *));
+_GL_CXXALIASWARN (cnd_init);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_init
+# if HAVE_RAW_DECL_CND_INIT
+_GL_WARN_ON_USE (cnd_init, "cnd_init is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_wait, int, (cnd_t *, mtx_t *) _GL_ARG_NONNULL ((1, 2)));
+# endif
+_GL_CXXALIAS_SYS (cnd_wait, int, (cnd_t *, mtx_t *));
+_GL_CXXALIASWARN (cnd_wait);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_wait
+# if HAVE_RAW_DECL_CND_WAIT
+_GL_WARN_ON_USE (cnd_wait, "cnd_wait is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_timedwait, int,
+                                 (cnd_t *, mtx_t *, const struct timespec *)
+                                 _GL_ARG_NONNULL ((1, 2, 3)));
+# endif
+_GL_CXXALIAS_SYS (cnd_timedwait, int,
+                                 (cnd_t *, mtx_t *, const struct timespec *));
+_GL_CXXALIASWARN (cnd_timedwait);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_timedwait
+# if HAVE_RAW_DECL_CND_TIMEDWAIT
+_GL_WARN_ON_USE (cnd_timedwait, "cnd_timedwait is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_signal, int, (cnd_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (cnd_signal, int, (cnd_t *));
+_GL_CXXALIASWARN (cnd_signal);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_signal
+# if HAVE_RAW_DECL_CND_SIGNAL
+_GL_WARN_ON_USE (cnd_signal, "cnd_signal is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_broadcast, int, (cnd_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (cnd_broadcast, int, (cnd_t *));
+_GL_CXXALIASWARN (cnd_broadcast);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_broadcast
+# if HAVE_RAW_DECL_CND_BROADCAST
+_GL_WARN_ON_USE (cnd_broadcast, "cnd_broadcast is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+#if @GNULIB_CND@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (cnd_destroy, void, (cnd_t *) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (cnd_destroy, void, (cnd_t *));
+_GL_CXXALIASWARN (cnd_destroy);
+#elif defined GNULIB_POSIXCHECK
+# undef cnd_destroy
+# if HAVE_RAW_DECL_CND_DESTROY
+_GL_WARN_ON_USE (cnd_destroy, "cnd_destroy is unportable - "
+                 "use gnulib module cnd for portability");
+# endif
+#endif
+
+
+/* =========== ISO C 11 7.26.6 Thread-specific storage functions =========== */
+
+#if !@HAVE_THREADS_H@
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  include "windows-tls.h"
+
+typedef glwthread_tls_key_t tss_t;
+#  define TSS_DTOR_ITERATIONS 0    /* Destructors are currently unsupported.  */
+
+# else
+/* Use POSIX threads.  */
+
+#  include <limits.h>
+
+typedef pthread_key_t tss_t;
+
+# endif
+
+/* Type for the destructor of a thread-specific storage pointer.  */
+typedef void (*tss_dtor_t) (void *);
+
+#endif
+
+/* AIX 7.1 <threads.h> does not define TSS_DTOR_ITERATIONS.  */
+#ifndef TSS_DTOR_ITERATIONS
+# ifdef PTHREAD_DESTRUCTOR_ITERATIONS
+#  define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
+# else
+   /* IRIX 6.5 does not define PTHREAD_DESTRUCTOR_ITERATIONS.
+      This value is a wild guess.  */
+#  define TSS_DTOR_ITERATIONS 1
+# endif
+#endif
+
+#if @GNULIB_TSS@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (tss_create, int, (tss_t *, tss_dtor_t) _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (tss_create, int, (tss_t *, tss_dtor_t));
+_GL_CXXALIASWARN (tss_create);
+#elif defined GNULIB_POSIXCHECK
+# undef tss_create
+# if HAVE_RAW_DECL_TSS_CREATE
+_GL_WARN_ON_USE (tss_create, "tss_create is unportable - "
+                 "use gnulib module tss for portability");
+# endif
+#endif
+
+#if @GNULIB_TSS@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (tss_set, int, (tss_t, void *));
+# endif
+_GL_CXXALIAS_SYS (tss_set, int, (tss_t, void *));
+_GL_CXXALIASWARN (tss_set);
+#elif defined GNULIB_POSIXCHECK
+# undef tss_set
+# if HAVE_RAW_DECL_TSS_SET
+_GL_WARN_ON_USE (tss_set, "tss_set is unportable - "
+                 "use gnulib module tss for portability");
+# endif
+#endif
+
+#if @GNULIB_TSS@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (tss_get, void *, (tss_t));
+# endif
+_GL_CXXALIAS_SYS (tss_get, void *, (tss_t));
+_GL_CXXALIASWARN (tss_get);
+#elif defined GNULIB_POSIXCHECK
+# undef tss_get
+# if HAVE_RAW_DECL_TSS_GET
+_GL_WARN_ON_USE (tss_get, "tss_get is unportable - "
+                 "use gnulib module tss for portability");
+# endif
+#endif
+
+#if @GNULIB_TSS@
+# if !@HAVE_THREADS_H@
+_GL_FUNCDECL_SYS (tss_delete, void, (tss_t));
+# endif
+_GL_CXXALIAS_SYS (tss_delete, void, (tss_t));
+_GL_CXXALIASWARN (tss_delete);
+#elif defined GNULIB_POSIXCHECK
+# undef tss_delete
+# if HAVE_RAW_DECL_TSS_DELETE
+_GL_WARN_ON_USE (tss_delete, "tss_delete is unportable - "
+                 "use gnulib module tss for portability");
+# endif
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_THREADS_H */
+#endif /* _@GUARD_PREFIX@_THREADS_H */
diff --git a/m4/threads.m4 b/m4/threads.m4
new file mode 100644
index 0000000..3cb3ae1
--- /dev/null
+++ b/m4/threads.m4
@@ -0,0 +1,126 @@
+# threads.m4 serial 1
+dnl Copyright (C) 2019 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_THREADS_H],
+[
+  AC_REQUIRE([gl_THREADS_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([gl_THREADLIB_BODY])
+  AC_REQUIRE([gl_YIELD])
+
+  gl_CHECK_NEXT_HEADERS([threads.h])
+  if test $ac_cv_header_threads_h = yes; then
+    HAVE_THREADS_H=1
+  else
+    HAVE_THREADS_H=0
+  fi
+  AC_SUBST([HAVE_THREADS_H])
+
+  if test $HAVE_THREADS_H = 1; then
+    dnl AIX 7.1..7.2 defines thrd_start_t incorrectly, namely as
+    dnl 'void * (*) (void *)' instead of 'int (*) (void *)'.
+    AC_CACHE_CHECK([whether thrd_start_t is correct],
+      [gl_cv_thrd_start_t_correct],
+      [AC_COMPILE_IFELSE(
+         [AC_LANG_PROGRAM([[
+            #include <threads.h>
+            #ifdef __cplusplus
+            extern "C" {
+            #endif
+            extern void foo (thrd_start_t);
+            extern void foo (int (*) (void *));
+            #ifdef __cplusplus
+            }
+            #endif
+          ]], [[]])],
+         [gl_cv_thrd_start_t_correct=yes],
+         [gl_cv_thrd_start_t_correct=no])
+      ])
+    if test $gl_cv_thrd_start_t_correct != yes; then
+      BROKEN_THRD_START_T=1
+    fi
+  fi
+
+  case "$host_os" in
+    mingw*)
+      LIBSTDTHREAD=
+      LTLIBSTDTHREAD=
+      ;;
+    *)
+      if test $ac_cv_header_threads_h = yes; then
+        dnl glibc >= 2.29 has thrd_create in libpthread.
+        dnl FreeBSD >= 10 has thrd_create in libstdthreads.
+        dnl AIX >= 7.1 and Solaris >= 11.4 have thrd_create in libc.
+        AC_CHECK_FUNCS([thrd_create])
+        if test $ac_cv_func_thrd_create = yes; then
+          LIBSTDTHREAD=
+          LTLIBSTDTHREAD=
+        else
+          AC_CHECK_LIB([stdthreads], [thrd_create], [
+            LIBSTDTHREAD='-lstdthreads'
+            LTLIBSTDTHREAD='-lstdthreads'
+          ], [
+            dnl Guess that thrd_create is in libpthread.
+            LIBSTDTHREAD="$LIBMULTITHREAD"
+            LTLIBSTDTHREAD="$LTLIBMULTITHREAD"
+          ])
+        fi
+      else
+        dnl Libraries needed by thrd.c, mtx.c, cnd.c, tss.c.
+        LIBSTDTHREAD="$LIBMULTITHREAD $YIELD_LIB"
+        LTLIBSTDTHREAD="$LTLIBMULTITHREAD $YIELD_LIB"
+      fi
+      ;;
+  esac
+  AC_SUBST([LIBSTDTHREAD])
+  AC_SUBST([LTLIBSTDTHREAD])
+
+  AH_VERBATIM([thread_local],
+[/* The _Thread_local keyword of C11.  */
+#ifndef _Thread_local
+# if defined __GNUC__
+#  define _Thread_local __thread
+# elif defined _MSC_VER
+#  define _Thread_local __declspec (thread)
+# endif
+#endif
+])
+
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use, and which is not
+  dnl guaranteed by C89.
+  gl_WARN_ON_USE_PREPARE([[#include <threads.h>
+    ]], [call_once
+    cnd_broadcast cnd_destroy cnd_init cnd_signal cnd_timedwait cnd_wait
+    mtx_destroy mtx_init mtx_lock mtx_timedlock mtx_trylock mtx_unlock
+    thrd_create thrd_current thrd_detach thrd_equal thrd_exit thrd_join
+    thrd_sleep thrd_yield
+    tss_create tss_delete tss_get tss_set])
+])
+
+AC_DEFUN([gl_THREADS_MODULE_INDICATOR],
+[
+  dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
+  AC_REQUIRE([gl_THREADS_H_DEFAULTS])
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+AC_DEFUN([gl_THREADS_H_DEFAULTS],
+[
+  GNULIB_CND=0;           AC_SUBST([GNULIB_CND])
+  GNULIB_MTX=0;           AC_SUBST([GNULIB_MTX])
+  GNULIB_THRD=0;          AC_SUBST([GNULIB_THRD])
+  GNULIB_TSS=0;           AC_SUBST([GNULIB_TSS])
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  BROKEN_THRD_START_T=0;  AC_SUBST([BROKEN_THRD_START_T])
+  REPLACE_THRD_CREATE=0;  AC_SUBST([REPLACE_THRD_CREATE])
+  REPLACE_THRD_CURRENT=0; AC_SUBST([REPLACE_THRD_CURRENT])
+  REPLACE_THRD_DETACH=0;  AC_SUBST([REPLACE_THRD_DETACH])
+  REPLACE_THRD_EQUAL=0;   AC_SUBST([REPLACE_THRD_EQUAL])
+  REPLACE_THRD_JOIN=0;    AC_SUBST([REPLACE_THRD_JOIN])
+])
diff --git a/m4/yield.m4 b/m4/yield.m4
index 13f6f20..59c8c38 100644
--- a/m4/yield.m4
+++ b/m4/yield.m4
@@ -1,4 +1,4 @@
-# yield.m4 serial 2
+# yield.m4 serial 3
 dnl Copyright (C) 2005-2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -10,7 +10,7 @@ AC_DEFUN([gl_YIELD],
   dnl On some systems, sched_yield is in librt, rather than in libpthread.
   YIELD_LIB=
   if test $gl_threads_api = posix; then
-    dnl Solaris has sched_yield in librt, not in libpthread or libc.
+    dnl Solaris 7...10 has sched_yield in librt, not in libpthread or libc.
     AC_CHECK_LIB([rt], [sched_yield], [YIELD_LIB=-lrt],
       [dnl Solaris 2.5.1, 2.6 has sched_yield in libposix4, not librt.
        AC_CHECK_LIB([posix4], [sched_yield], [YIELD_LIB=-lposix4])])
diff --git a/modules/threads-h b/modules/threads-h
new file mode 100644
index 0000000..238955c
--- /dev/null
+++ b/modules/threads-h
@@ -0,0 +1,73 @@
+Description:
+An ISO C 11 compatible <threads.h>.
+
+Files:
+lib/threads.in.h
+lib/windows-thread.h
+lib/windows-mutex.h
+lib/windows-recmutex.h
+lib/windows-timedmutex.h
+lib/windows-timedrecmutex.h
+lib/windows-once.h
+lib/windows-cond.h
+lib/windows-tls.h
+lib/windows-spinlock.h
+m4/threads.m4
+m4/threadlib.m4
+m4/yield.m4
+build-aux/config.rpath
+
+Depends-on:
+include_next
+snippet/c++defs
+snippet/_Noreturn
+snippet/arg-nonnull
+snippet/warn-on-use
+time
+havelib
+
+configure.ac-early:
+gl_THREADLIB_EARLY
+
+configure.ac:
+AC_REQUIRE([gl_THREADS_H])
+
+Makefile.am:
+BUILT_SOURCES += threads.h
+
+# We need the following in order to create <threads.h> when the system
+# doesn't have one.
+threads.h: threads.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(_NORETURN_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
+	  sed -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
+	      -e 's|@''HAVE_THREADS_H''@|$(HAVE_THREADS_H)|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_THREADS_H''@|$(NEXT_THREADS_H)|g' \
+	      -e 's/@''GNULIB_CND''@/$(GNULIB_CND)/g' \
+	      -e 's/@''GNULIB_MTX''@/$(GNULIB_MTX)/g' \
+	      -e 's/@''GNULIB_THRD''@/$(GNULIB_THRD)/g' \
+	      -e 's/@''GNULIB_TSS''@/$(GNULIB_TSS)/g' \
+	      -e 's|@''BROKEN_THRD_START_T''@|$(BROKEN_THRD_START_T)|g' \
+	      -e 's|@''REPLACE_THRD_CREATE''@|$(REPLACE_THRD_CREATE)|g' \
+	      -e 's|@''REPLACE_THRD_CURRENT''@|$(REPLACE_THRD_CURRENT)|g' \
+	      -e 's|@''REPLACE_THRD_DETACH''@|$(REPLACE_THRD_DETACH)|g' \
+	      -e 's|@''REPLACE_THRD_EQUAL''@|$(REPLACE_THRD_EQUAL)|g' \
+	      -e 's|@''REPLACE_THRD_JOIN''@|$(REPLACE_THRD_JOIN)|g' \
+	      < $(srcdir)/threads.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += threads.h threads.h-t
+
+Include:
+<threads.h>
+
+Link:
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/yield b/modules/yield
index 24ffcdd..71e00ea 100644
--- a/modules/yield
+++ b/modules/yield
@@ -9,7 +9,7 @@ Depends-on:
 threadlib
 
 configure.ac:
-gl_YIELD
+AC_REQUIRE([gl_YIELD])
 
 Makefile.am:
 lib_SOURCES += glthread/yield.h
-- 
2.7.4


[-- Attachment #3: 0017-threads-h-Add-tests.patch --]
[-- Type: text/x-patch, Size: 7141 bytes --]

From bf6dd504cc2fcd8481ad5787999b845a3c00f54d Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:25:58 +0200
Subject: [PATCH 17/26] threads-h: Add tests.

* tests/test-threads.c: New file.
* modules/threads-h-tests: New file.
* tests/test-threads-c++.cc: New file.
* modules/threads-h-c++-tests: New file.
---
 ChangeLog                   |  8 +++++
 modules/threads-h-c++-tests | 19 ++++++++++++
 modules/threads-h-tests     | 12 ++++++++
 tests/test-threads-c++.cc   | 73 +++++++++++++++++++++++++++++++++++++++++++++
 tests/test-threads.c        | 65 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 177 insertions(+)
 create mode 100644 modules/threads-h-c++-tests
 create mode 100644 modules/threads-h-tests
 create mode 100644 tests/test-threads-c++.cc
 create mode 100644 tests/test-threads.c

diff --git a/ChangeLog b/ChangeLog
index 22b942b..b536bcb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	threads-h: Add tests.
+	* tests/test-threads.c: New file.
+	* modules/threads-h-tests: New file.
+	* tests/test-threads-c++.cc: New file.
+	* modules/threads-h-c++-tests: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	threads-h: New module.
 	* lib/threads.in.h: New file.
 	* m4/threads.m4: New file.
diff --git a/modules/threads-h-c++-tests b/modules/threads-h-c++-tests
new file mode 100644
index 0000000..8553bd6
--- /dev/null
+++ b/modules/threads-h-c++-tests
@@ -0,0 +1,19 @@
+Files:
+tests/test-threads-c++.cc
+tests/signature.h
+
+Status:
+c++-test
+
+Depends-on:
+ansi-c++-opt
+
+configure.ac:
+
+Makefile.am:
+if ANSICXX
+TESTS += test-threads-c++
+check_PROGRAMS += test-threads-c++
+test_threads_c___SOURCES = test-threads-c++.cc
+test_threads_c___LDADD = $(LDADD) @LIBSTDTHREAD@
+endif
diff --git a/modules/threads-h-tests b/modules/threads-h-tests
new file mode 100644
index 0000000..e4f399d
--- /dev/null
+++ b/modules/threads-h-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-threads.c
+
+Depends-on:
+threads-h-c++-tests
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-threads
+check_PROGRAMS += test-threads
+test_threads_LDADD = $(LDADD) @LIBSTDTHREAD@
diff --git a/tests/test-threads-c++.cc b/tests/test-threads-c++.cc
new file mode 100644
index 0000000..66877d4
--- /dev/null
+++ b/tests/test-threads-c++.cc
@@ -0,0 +1,73 @@
+/* Test of <threads.h> substitute in C++ mode.
+   Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
+
+#define GNULIB_NAMESPACE gnulib
+#include <config.h>
+
+#include <threads.h>
+
+#include "signature.h"
+
+
+#if GNULIB_TEST_THRD
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_create, int,
+                 (thrd_t *, thrd_start_t, void *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_current, thrd_t, (void));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_equal, int, (thrd_t, thrd_t));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_sleep, int,
+                 (const struct timespec *, struct timespec *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_yield, void, (void));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_detach, int, (thrd_t));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_join, int, (thrd_t, int *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::thrd_exit, _Noreturn void, (int));
+#endif
+
+#if GNULIB_TEST_MTX
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_init, int, (mtx *, int));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_lock, int, (mtx *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_trylock, int, (mtx *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_timedlock, int,
+                 (mtx *, const struct timespec *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_unlock, int, (mtx *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::mtx_destroy, void, (mtx *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::call_once, void,
+                 (once_flag *, void (*) (void)));
+#endif
+
+#if GNULIB_TEST_CND
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_init, int, (cnd_t *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_wait, int, (cnd_t *, mtx_t *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_timedwait, int,
+                 (cnd_t *, mtx_t *, const struct timespec *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_signal, int, (cnd_t *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_broadcast, int, (cnd_t *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::cnd_destroy, void, (cnd_t *));
+#endif
+
+#if GNULIB_TEST_TSS
+SIGNATURE_CHECK (GNULIB_NAMESPACE::tss_create, int, (tss_t *, tss_dtor_t));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::tss_set, int, (tss_t, void *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::tss_get, void *, (tss_t));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::tss_delete, void, (tss_t));
+#endif
+
+
+int
+main ()
+{
+}
diff --git a/tests/test-threads.c b/tests/test-threads.c
new file mode 100644
index 0000000..39a0f3c
--- /dev/null
+++ b/tests/test-threads.c
@@ -0,0 +1,65 @@
+/* Test of <threads.h> substitute.
+   Copyright (C) 2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+/* Check that thread_local is defined.  */
+#ifndef thread_local
+"oops"
+#endif
+
+/* Check that ONCE_FLAG_INIT is defined.  */
+#ifndef ONCE_FLAG_INIT
+"oops1"
+#endif
+once_flag a = ONCE_FLAG_INIT;
+
+/* Check that TSS_DTOR_ITERATIONS is defined.  */
+#ifndef TSS_DTOR_ITERATIONS
+"oops2"
+#endif
+int b = TSS_DTOR_ITERATIONS;
+
+int
+main (void)
+{
+  /* Ensure no overlap in thrd_*. */
+  switch (0)
+    {
+    case thrd_success:
+    case thrd_timedout:
+    case thrd_busy:
+    case thrd_nomem:
+    case thrd_error:
+      ;
+    }
+
+  /* Ensure no overlap among valid types for mtx_init. */
+  switch (0)
+    {
+    case mtx_plain:
+    case mtx_timed:
+    case mtx_plain | mtx_recursive:
+    case mtx_timed | mtx_recursive:
+      ;
+    }
+
+  return 0;
+}
-- 
2.7.4


[-- Attachment #4: 0018-thrd-New-module.patch --]
[-- Type: text/x-patch, Size: 24590 bytes --]

From 3696a934e60b3c390bc217bf3273e0c5399e5fd4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:26:31 +0200
Subject: [PATCH 18/26] thrd: New module.

* lib/thrd.c: New file.
* m4/thrd.m4: New file.
* modules/thrd: New file.
* doc/posix-functions/thrd_current.texi: Mention the new module.
* doc/posix-functions/thrd_detach.texi: Likewise.
* doc/posix-functions/thrd_equal.texi: Likewise.
* doc/posix-functions/thrd_exit.texi: Likewise.
* doc/posix-functions/thrd_sleep.texi: Likewise.
* doc/posix-functions/thrd_yield.texi: Likewise.
* doc/posix-functions/thrd_create.texi: Mention the new module and the
AIX bug.
* doc/posix-functions/thrd_join.texi: Mention the new module and the
AIX and Solaris bugs.
---
 ChangeLog                             |  17 ++
 doc/posix-functions/thrd_create.texi  |  11 +-
 doc/posix-functions/thrd_current.texi |   8 +-
 doc/posix-functions/thrd_detach.texi  |   8 +-
 doc/posix-functions/thrd_equal.texi   |   8 +-
 doc/posix-functions/thrd_exit.texi    |   8 +-
 doc/posix-functions/thrd_join.texi    |  14 +-
 doc/posix-functions/thrd_sleep.texi   |   8 +-
 doc/posix-functions/thrd_yield.texi   |   8 +-
 lib/thrd.c                            | 427 ++++++++++++++++++++++++++++++++++
 m4/thrd.m4                            |  63 +++++
 modules/thrd                          |  33 +++
 12 files changed, 581 insertions(+), 32 deletions(-)
 create mode 100644 lib/thrd.c
 create mode 100644 m4/thrd.m4
 create mode 100644 modules/thrd

diff --git a/ChangeLog b/ChangeLog
index b536bcb..5d96988 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	thrd: New module.
+	* lib/thrd.c: New file.
+	* m4/thrd.m4: New file.
+	* modules/thrd: New file.
+	* doc/posix-functions/thrd_current.texi: Mention the new module.
+	* doc/posix-functions/thrd_detach.texi: Likewise.
+	* doc/posix-functions/thrd_equal.texi: Likewise.
+	* doc/posix-functions/thrd_exit.texi: Likewise.
+	* doc/posix-functions/thrd_sleep.texi: Likewise.
+	* doc/posix-functions/thrd_yield.texi: Likewise.
+	* doc/posix-functions/thrd_create.texi: Mention the new module and the
+	AIX bug.
+	* doc/posix-functions/thrd_join.texi: Mention the new module and the
+	AIX and Solaris bugs.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	threads-h: Add tests.
 	* tests/test-threads.c: New file.
 	* modules/threads-h-tests: New file.
diff --git a/doc/posix-functions/thrd_create.texi b/doc/posix-functions/thrd_create.texi
index eb3fd50..9fe35f4 100644
--- a/doc/posix-functions/thrd_create.texi
+++ b/doc/posix-functions/thrd_create.texi
@@ -10,15 +10,18 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
+@item
+This function uses an incorrectly defined @code{thrd_start_t} on some platforms:
+AIX 7.2.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_current.texi b/doc/posix-functions/thrd_current.texi
index 03f33a5..ea81185 100644
--- a/doc/posix-functions/thrd_current.texi
+++ b/doc/posix-functions/thrd_current.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_detach.texi b/doc/posix-functions/thrd_detach.texi
index 3cfb318..bb8f6c3 100644
--- a/doc/posix-functions/thrd_detach.texi
+++ b/doc/posix-functions/thrd_detach.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_equal.texi b/doc/posix-functions/thrd_equal.texi
index 58f5a02..18555024 100644
--- a/doc/posix-functions/thrd_equal.texi
+++ b/doc/posix-functions/thrd_equal.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_exit.texi b/doc/posix-functions/thrd_exit.texi
index a2683b6..75ae888 100644
--- a/doc/posix-functions/thrd_exit.texi
+++ b/doc/posix-functions/thrd_exit.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_join.texi b/doc/posix-functions/thrd_join.texi
index 2199a8f..51b2ec9 100644
--- a/doc/posix-functions/thrd_join.texi
+++ b/doc/posix-functions/thrd_join.texi
@@ -10,15 +10,21 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
+@item
+This function never stores an exit code on some platforms:
+AIX 7.2.
+@item
+This function crashes when the second argument is NULL on some platforms:
+Solaris 11.4.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_sleep.texi b/doc/posix-functions/thrd_sleep.texi
index afdb3ea..5e02eb1 100644
--- a/doc/posix-functions/thrd_sleep.texi
+++ b/doc/posix-functions/thrd_sleep.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/thrd_yield.texi b/doc/posix-functions/thrd_yield.texi
index a787a69..dde2a71 100644
--- a/doc/posix-functions/thrd_yield.texi
+++ b/doc/posix-functions/thrd_yield.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread-Management.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: thrd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/lib/thrd.c b/lib/thrd.c
new file mode 100644
index 0000000..e8f29bc
--- /dev/null
+++ b/lib/thrd.c
@@ -0,0 +1,427 @@
+/* Creating and controlling ISO C 11 threads.
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include <stdlib.h>
+
+#if HAVE_THREADS_H
+/* Provide workarounds.  */
+
+# if BROKEN_THRD_START_T
+
+#  undef thrd_t
+
+/* AIX 7.1..7.2 defines thrd_start_t incorrectly, namely as
+   'void * (*) (void *)' instead of 'int (*) (void *)'.
+   As a consequence, its thrd_join function never stores an exit code.  */
+
+/* The Thread-Specific Storage (TSS) key that allows to access each thread's
+   'struct thrd_with_exitcode *' pointer.  */
+static tss_t thrd_with_exitcode_key;
+
+/* Initializes thrd_with_exitcode_key.
+   This function must only be called once.  */
+static void
+do_init_thrd_with_exitcode_key (void)
+{
+  if (tss_create (&thrd_with_exitcode_key, NULL) != thrd_success)
+    abort ();
+}
+
+/* Initializes thrd_with_exitcode_key.  */
+static void
+init_thrd_with_exitcode_key (void)
+{
+  static once_flag once = ONCE_FLAG_INIT;
+  call_once (&once, do_init_thrd_with_exitcode_key);
+}
+
+typedef union
+        {
+          struct thrd_with_exitcode t;
+          struct
+          {
+            thrd_t tid; /* reserve memory for t.tid */
+            int detached; /* reserve memory for t.detached */
+            thrd_start_t mainfunc;
+            void *arg;
+          } a;
+        }
+        main_arg_t;
+
+static void *
+thrd_main_func (void *pmarg)
+{
+  /* Unpack the object that combines mainfunc and arg.  */
+  main_arg_t *main_arg = (main_arg_t *) pmarg;
+  thrd_start_t mainfunc = main_arg->a.mainfunc;
+  void *arg = main_arg->a.arg;
+
+  if (tss_set (thrd_with_exitcode_key, &main_arg->t) != thrd_success)
+    abort ();
+
+  /* Execute mainfunc, with arg as argument.  */
+  {
+    int exitcode = mainfunc (arg);
+    /* Store the exitcode, for use by thrd_join().  */
+    main_arg->t.exitcode = exitcode;
+    if (main_arg->t.detached)
+      {
+        /* Clean up the thread, like thrd_join would do.  */
+        free (&main_arg->t);
+      }
+    return NULL;
+  }
+}
+
+int
+rpl_thrd_create (rpl_thrd_t *threadp, thrd_start_t mainfunc, void *arg)
+#  undef thrd_create
+{
+  init_thrd_with_exitcode_key ();
+  {
+    /* Combine mainfunc and arg in a single object.
+       A stack-allocated object does not work, because it would be out of
+       existence when thrd_create returns before pthread_main_func is
+       entered.  So, allocate it in the heap.  */
+    main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
+    if (main_arg == NULL)
+      return thrd_nomem;
+    main_arg->a.mainfunc = mainfunc;
+    main_arg->a.arg = arg;
+    main_arg->t.detached = 0;
+    {
+      int err =
+        thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg);
+      if (err == thrd_success)
+        *threadp = &main_arg->t;
+      else
+        free (main_arg);
+      return err;
+    }
+  }
+}
+
+rpl_thrd_t
+rpl_thrd_current (void)
+#  undef thrd_current
+{
+  init_thrd_with_exitcode_key ();
+  {
+    rpl_thrd_t thread =
+      (struct thrd_with_exitcode *) tss_get (thrd_with_exitcode_key);
+    if (thread == NULL)
+      {
+        /* This happens only in threads that have not been created through
+           thrd_create(), such as the main thread.  */
+        for (;;)
+          {
+            thread =
+              (struct thrd_with_exitcode *)
+              malloc (sizeof (struct thrd_with_exitcode));
+            if (thread != NULL)
+              break;
+            /* Memory allocation failed.  There is not much we can do.  Have to
+               busy-loop, waiting for the availability of memory.  */
+            {
+              struct timespec ts;
+              ts.tv_sec = 1;
+              ts.tv_nsec = 0;
+              thrd_sleep (&ts, NULL);
+            }
+          }
+        thread->tid = thrd_current ();
+        thread->detached = 0; /* This can lead to a memory leak.  */
+        thread->exitcode = 0; /* just to be deterministic */
+        if (tss_set (thrd_with_exitcode_key, thread) != thrd_success)
+          abort ();
+      }
+    return thread;
+  }
+}
+
+int
+rpl_thrd_equal (rpl_thrd_t thread1, rpl_thrd_t thread2)
+{
+  return thread1 == thread2;
+}
+
+int
+rpl_thrd_detach (rpl_thrd_t thread)
+#  undef thrd_detach
+{
+  if (thread->detached)
+    return thrd_error;
+  {
+    int err =
+      thrd_detach (thread == rpl_thrd_current ()
+                   ? /* thread->tid may not be initialized at this point.  */
+                     thrd_current ()
+                   : thread->tid);
+    if (err == thrd_success)
+      thread->detached = 1;
+    return err;
+  }
+}
+
+int
+rpl_thrd_join (rpl_thrd_t thread, int *exitcodep)
+#  undef thrd_join
+{
+  if (thread == rpl_thrd_current () || thread->detached)
+    return thrd_error;
+  {
+    int err = thrd_join (thread->tid, NULL);
+    if (err == thrd_success)
+      {
+        if (exitcodep != NULL)
+          *exitcodep = thread->exitcode;
+        free (thread);
+      }
+    return err;
+  }
+}
+
+# endif
+
+# if BROKEN_THRD_JOIN
+
+/* On Solaris 11.4, thrd_join crashes when the second argument is NULL.  */
+int
+rpl_thrd_join (thrd_t thread, int *exitcodep)
+#  undef thrd_join
+{
+  int exitcode;
+  int err = thrd_join (thread, &exitcode);
+  if (err == 0 && exitcodep != NULL)
+    *exitcodep = exitcode;
+  return err;
+}
+
+# endif
+
+#else
+
+# include <errno.h>
+# include <stdint.h>
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+#  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+#  include <windows.h>
+
+# else
+/* Use POSIX threads.  */
+
+#  include <pthread.h>
+#  include <sched.h>
+
+# endif
+
+/* The main functions passed to thrd_create and
+   pthread_create/glwthread_thread_create have different return types:
+   'int' vs. 'void *'.  */
+
+struct pthread_main_arg_t
+{
+  thrd_start_t mainfunc;
+  void *arg;
+};
+
+static void *
+pthread_main_func (void *pmarg)
+{
+  /* Unpack the object that combines mainfunc and arg.  */
+  struct pthread_main_arg_t *pthread_main_arg =
+    (struct pthread_main_arg_t *) pmarg;
+  thrd_start_t mainfunc = pthread_main_arg->mainfunc;
+  void *arg = pthread_main_arg->arg;
+
+  /* Free it.  */
+  free (pmarg);
+
+  /* Execute mainfunc, with arg as argument.  */
+  {
+    int exitcode = mainfunc (arg);
+    return (void *) (intptr_t) exitcode;
+  }
+}
+
+# if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+int
+thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
+{
+  /* Combine mainfunc and arg in a single object.
+     A stack-allocated object does not work, because it would be out of
+     existence when thrd_create returns before pthread_main_func is
+     entered.  So, allocate it in the heap.  */
+  struct pthread_main_arg_t *pthread_main_arg =
+    (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
+  if (pthread_main_arg == NULL)
+    return thrd_nomem;
+  pthread_main_arg->mainfunc = mainfunc;
+  pthread_main_arg->arg = arg;
+
+  {
+    int err = glwthread_thread_create (threadp,
+                                       pthread_main_func, pthread_main_arg);
+    if (err != 0)
+      free (pthread_main_arg);
+    return (err == 0 ? thrd_success :
+            err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
+            thrd_error);
+  }
+}
+
+thrd_t
+thrd_current (void)
+{
+  return glwthread_thread_self ();
+}
+
+int
+thrd_equal (thrd_t thread1, thrd_t thread2)
+{
+  return thread1 == thread2;
+}
+
+void
+thrd_yield (void)
+{
+  Sleep (0);
+}
+
+int
+thrd_detach (thrd_t thread)
+{
+  int err = glwthread_thread_detach (thread);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+thrd_join (thrd_t thread, int *exitcodep)
+{
+  void *exitptr;
+  int err = glwthread_thread_join (thread, &exitptr);
+  if (err == 0)
+    {
+      if (exitcodep != NULL)
+        *exitcodep = (int) (intptr_t) exitptr;
+      return thrd_success;
+    }
+  else
+    return thrd_error;
+}
+
+_Noreturn void
+thrd_exit (int exitcode)
+{
+  glwthread_thread_exit ((void *) (intptr_t) exitcode);
+}
+
+# else
+/* Use POSIX threads.  */
+
+int
+thrd_create (thrd_t *threadp, thrd_start_t mainfunc, void *arg)
+{
+  /* Combine mainfunc and arg in a single object.
+     A stack-allocated object does not work, because it would be out of
+     existence when thrd_create returns before pthread_main_func is
+     entered.  So, allocate it in the heap.  */
+  struct pthread_main_arg_t *pthread_main_arg =
+    (struct pthread_main_arg_t *) malloc (sizeof (struct pthread_main_arg_t));
+  if (pthread_main_arg == NULL)
+    return thrd_nomem;
+  pthread_main_arg->mainfunc = mainfunc;
+  pthread_main_arg->arg = arg;
+
+  {
+    int err = pthread_create (threadp, NULL,
+                              pthread_main_func, pthread_main_arg);
+    if (err != 0)
+      free (pthread_main_arg);
+    return (err == 0 ? thrd_success :
+            err == ENOMEM /* || err == EAGAIN */ ? thrd_nomem :
+            thrd_error);
+  }
+}
+
+thrd_t
+thrd_current (void)
+{
+  return pthread_self ();
+}
+
+int
+thrd_equal (thrd_t thread1, thrd_t thread2)
+{
+  return pthread_equal (thread1, thread2);
+}
+
+void
+thrd_yield (void)
+{
+  sched_yield ();
+}
+
+int
+thrd_detach (thrd_t thread)
+{
+  int err = pthread_detach (thread);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+thrd_join (thrd_t thread, int *exitcodep)
+{
+  void *exitptr;
+  int err = pthread_join (thread, &exitptr);
+  if (err == 0)
+    {
+      if (exitcodep != NULL)
+        *exitcodep = (int) (intptr_t) exitptr;
+      return thrd_success;
+    }
+  else
+    return thrd_error;
+}
+
+_Noreturn void
+thrd_exit (int exitcode)
+{
+  pthread_exit ((void *) (intptr_t) exitcode);
+}
+
+# endif
+
+int
+thrd_sleep (const struct timespec *duration, struct timespec *remaining)
+{
+  int ret = nanosleep (duration, remaining);
+  return (ret == 0 ? 0 : errno == EINTR ? -1 : -2);
+}
+
+#endif
diff --git a/m4/thrd.m4 b/m4/thrd.m4
new file mode 100644
index 0000000..6e9c8f4
--- /dev/null
+++ b/m4/thrd.m4
@@ -0,0 +1,63 @@
+# thrd.m4 serial 1
+dnl Copyright (C) 2019 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_FUNC_THRD_JOIN],
+[
+  AC_REQUIRE([gl_THREADS_H])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  if test $ac_cv_header_threads_h = yes; then
+    if test $BROKEN_THRD_START_T = 1; then
+      REPLACE_THRD_CREATE=1
+      REPLACE_THRD_CURRENT=1
+      REPLACE_THRD_DETACH=1
+      REPLACE_THRD_EQUAL=1
+      REPLACE_THRD_JOIN=1
+      AC_DEFINE([BROKEN_THRD_START_T], [1],
+        [Define if the thrd_start_t type is not as described in ISO C 11.])
+    fi
+
+    dnl On Solaris 11.4, thrd_join crashes when the second argument is NULL.
+    AC_CACHE_CHECK([whether thrd_join works],
+      [gl_cv_func_thrd_join_works],
+      [save_LIBS="$LIBS"
+       LIBS="$LIBS $LIBSTDTHREAD"
+       AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <stddef.h>
+              #include <threads.h>
+              static int func (void *arg)
+              {
+                return (arg != NULL);
+              }
+            ]],
+            [[thrd_t thread;
+              if (thrd_create (&thread, func, NULL) != thrd_success)
+                return 1;
+              if (thrd_join (thread, NULL) != thrd_success)
+                return 2;
+              return 0;
+            ]])],
+         [gl_cv_func_thrd_join_works=yes],
+         [gl_cv_func_thrd_join_works=no],
+         [case "$host_os" in
+                      # Only Solaris is known to be broken.
+            solaris*) gl_cv_func_thrd_join_works="guessing no" ;;
+            *)        gl_cv_func_thrd_join_works="guessing yes" ;;
+          esac
+         ])
+       LIBS="$save_LIBS"
+      ])
+    case "$gl_cv_func_thrd_join_works" in
+      *yes) ;;
+      *)
+        REPLACE_THRD_JOIN=1
+        AC_DEFINE([BROKEN_THRD_JOIN], [1],
+          [Define if the thrd_join function does not behave as in ISO C 11.])
+        ;;
+    esac
+  fi
+])
diff --git a/modules/thrd b/modules/thrd
new file mode 100644
index 0000000..3fd6063
--- /dev/null
+++ b/modules/thrd
@@ -0,0 +1,33 @@
+Description:
+ISO C 11 thread functions.
+
+Files:
+lib/thrd.c
+m4/thrd.m4
+
+Depends-on:
+threads-h
+windows-thread
+nanosleep
+
+configure.ac:
+AC_REQUIRE([gl_THREADS_H])
+gl_FUNC_THRD_JOIN
+if test $HAVE_THREADS_H = 0 || test $REPLACE_THRD_CREATE = 1 || test $REPLACE_THRD_JOIN = 1; then
+  AC_LIBOBJ([thrd])
+fi
+gl_THREADS_MODULE_INDICATOR([thrd])
+
+Makefile.am:
+
+Include:
+<threads.h>
+
+Link:
+$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #5: 0019-mtx-New-module.patch --]
[-- Type: text/x-patch, Size: 16614 bytes --]

From 6b4c64f06648b95f942d5428991979b9d8f2c69b Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:30:11 +0200
Subject: [PATCH 19/26] mtx: New module.

* lib/mtx.c: New file.
* modules/mtx: New file.
* doc/posix-functions/call_once.texi: Mention the new module.
* doc/posix-functions/mtx_init.texi: Likewise.
* doc/posix-functions/mtx_lock.texi: Likewise.
* doc/posix-functions/mtx_trylock.texi: Likewise.
* doc/posix-functions/mtx_timedlock.texi: Likewise.
* doc/posix-functions/mtx_unlock.texi: Likewise.
* doc/posix-functions/mtx_destroy.texi: Likewise.
---
 ChangeLog                              |  13 ++
 doc/posix-functions/call_once.texi     |   8 +-
 doc/posix-functions/mtx_destroy.texi   |   8 +-
 doc/posix-functions/mtx_init.texi      |   8 +-
 doc/posix-functions/mtx_lock.texi      |   8 +-
 doc/posix-functions/mtx_timedlock.texi |   8 +-
 doc/posix-functions/mtx_trylock.texi   |   8 +-
 doc/posix-functions/mtx_unlock.texi    |   8 +-
 lib/mtx.c                              | 288 +++++++++++++++++++++++++++++++++
 modules/mtx                            |  34 ++++
 10 files changed, 363 insertions(+), 28 deletions(-)
 create mode 100644 lib/mtx.c
 create mode 100644 modules/mtx

diff --git a/ChangeLog b/ChangeLog
index 5d96988..0e73bd6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	mtx: New module.
+	* lib/mtx.c: New file.
+	* modules/mtx: New file.
+	* doc/posix-functions/call_once.texi: Mention the new module.
+	* doc/posix-functions/mtx_init.texi: Likewise.
+	* doc/posix-functions/mtx_lock.texi: Likewise.
+	* doc/posix-functions/mtx_trylock.texi: Likewise.
+	* doc/posix-functions/mtx_timedlock.texi: Likewise.
+	* doc/posix-functions/mtx_unlock.texi: Likewise.
+	* doc/posix-functions/mtx_destroy.texi: Likewise.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	thrd: New module.
 	* lib/thrd.c: New file.
 	* m4/thrd.m4: New file.
diff --git a/doc/posix-functions/call_once.texi b/doc/posix-functions/call_once.texi
index c3b10fb..84b9dfe 100644
--- a/doc/posix-functions/call_once.texi
+++ b/doc/posix-functions/call_once.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/Call-Once.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_destroy.texi b/doc/posix-functions/mtx_destroy.texi
index ec8fe87..fda5048 100644
--- a/doc/posix-functions/mtx_destroy.texi
+++ b/doc/posix-functions/mtx_destroy.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_init.texi b/doc/posix-functions/mtx_init.texi
index 867f6a2..297ad34 100644
--- a/doc/posix-functions/mtx_init.texi
+++ b/doc/posix-functions/mtx_init.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_lock.texi b/doc/posix-functions/mtx_lock.texi
index 8e43992..958589d 100644
--- a/doc/posix-functions/mtx_lock.texi
+++ b/doc/posix-functions/mtx_lock.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_timedlock.texi b/doc/posix-functions/mtx_timedlock.texi
index 31e766e..d42f1ae 100644
--- a/doc/posix-functions/mtx_timedlock.texi
+++ b/doc/posix-functions/mtx_timedlock.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_trylock.texi b/doc/posix-functions/mtx_trylock.texi
index 26c38d6..e5beb64 100644
--- a/doc/posix-functions/mtx_trylock.texi
+++ b/doc/posix-functions/mtx_trylock.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/mtx_unlock.texi b/doc/posix-functions/mtx_unlock.texi
index e7779d8..f77ea17 100644
--- a/doc/posix-functions/mtx_unlock.texi
+++ b/doc/posix-functions/mtx_unlock.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Mutexes.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: mtx
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/lib/mtx.c b/lib/mtx.c
new file mode 100644
index 0000000..6acb8ae
--- /dev/null
+++ b/lib/mtx.c
@@ -0,0 +1,288 @@
+/* ISO C 11 locking in multithreaded situations.
+   Copyright (C) 2005-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.
+   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include <errno.h>
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+# define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+# include <windows.h>
+
+# include <stdlib.h>
+
+#else
+/* Use POSIX threads.  */
+
+# include <pthread.h>
+
+#endif
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+int
+mtx_init (mtx_t *mutex, int type)
+{
+  switch (type)
+    {
+    case mtx_plain:
+      glwthread_mutex_init (&mutex->u.u_mutex);
+      break;
+    case mtx_plain | mtx_recursive:
+      glwthread_recmutex_init (&mutex->u.u_recmutex);
+      break;
+    case mtx_timed:
+      if (glwthread_timedmutex_init (&mutex->u.u_timedmutex) != 0)
+        return thrd_error;
+      break;
+    case mtx_timed | mtx_recursive:
+      if (glwthread_timedrecmutex_init (&mutex->u.u_timedrecmutex) != 0)
+        return thrd_error;
+      break;
+    default:
+      return thrd_error;
+    }
+  mutex->type = type;
+  return thrd_success;
+}
+
+int
+mtx_lock (mtx_t *mutex)
+{
+  int err;
+
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      err = glwthread_mutex_lock (&mutex->u.u_mutex);
+      break;
+    case mtx_plain | mtx_recursive:
+      err = glwthread_recmutex_lock (&mutex->u.u_recmutex);
+      break;
+    case mtx_timed:
+      err = glwthread_timedmutex_lock (&mutex->u.u_timedmutex);
+      break;
+    case mtx_timed | mtx_recursive:
+      err = glwthread_timedrecmutex_lock (&mutex->u.u_timedrecmutex);
+      break;
+    default:
+      abort ();
+    }
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+mtx_trylock (mtx_t *mutex)
+{
+  int err;
+
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      err = glwthread_mutex_trylock (&mutex->u.u_mutex);
+      break;
+    case mtx_plain | mtx_recursive:
+      err = glwthread_recmutex_trylock (&mutex->u.u_recmutex);
+      break;
+    case mtx_timed:
+      err = glwthread_timedmutex_trylock (&mutex->u.u_timedmutex);
+      break;
+    case mtx_timed | mtx_recursive:
+      err = glwthread_timedrecmutex_trylock (&mutex->u.u_timedrecmutex);
+      break;
+    default:
+      abort ();
+    }
+  return (err == 0 ? thrd_success : err == EBUSY ? thrd_busy : thrd_error);
+}
+
+int
+mtx_timedlock (mtx_t *mutex, const struct timespec *abstime)
+{
+  int err;
+
+  switch (mutex->type)
+    {
+    case mtx_plain:
+    case mtx_plain | mtx_recursive:
+      return thrd_error;
+    case mtx_timed:
+      err = glwthread_timedmutex_timedlock (&mutex->u.u_timedmutex, abstime);
+      break;
+    case mtx_timed | mtx_recursive:
+      err =
+        glwthread_timedrecmutex_timedlock (&mutex->u.u_timedrecmutex, abstime);
+      break;
+    default:
+      abort ();
+    }
+  return (err == 0 ? thrd_success : err == EBUSY ? thrd_busy : thrd_error);
+}
+
+int
+mtx_unlock (mtx_t *mutex)
+{
+  int err;
+
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      err = glwthread_mutex_unlock (&mutex->u.u_mutex);
+      break;
+    case mtx_plain | mtx_recursive:
+      err = glwthread_recmutex_unlock (&mutex->u.u_recmutex);
+      break;
+    case mtx_timed:
+      err = glwthread_timedmutex_unlock (&mutex->u.u_timedmutex);
+      break;
+    case mtx_timed | mtx_recursive:
+      err = glwthread_timedrecmutex_unlock (&mutex->u.u_timedrecmutex);
+      break;
+    default:
+      abort ();
+    }
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void
+mtx_destroy (mtx_t *mutex)
+{
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      glwthread_mutex_destroy (&mutex->u.u_mutex);
+      break;
+    case mtx_plain | mtx_recursive:
+      glwthread_recmutex_destroy (&mutex->u.u_recmutex);
+      break;
+    case mtx_timed:
+      glwthread_timedmutex_destroy (&mutex->u.u_timedmutex);
+      break;
+    case mtx_timed | mtx_recursive:
+      glwthread_timedrecmutex_destroy (&mutex->u.u_timedrecmutex);
+      break;
+    default:
+      abort ();
+    }
+}
+
+void
+call_once (once_flag *flagp, void (*func) (void))
+{
+  glwthread_once (flagp, func);
+}
+
+#else
+/* Use POSIX threads.  */
+
+int
+mtx_init (mtx_t *mutex, int type)
+{
+  switch (type)
+    {
+    case mtx_plain:
+    case mtx_timed:
+    case mtx_plain | mtx_recursive:
+    case mtx_timed | mtx_recursive:
+      break;
+    default:
+      return thrd_error;
+    }
+
+  if ((type & mtx_recursive) != 0)
+    {
+      pthread_mutexattr_t attributes;
+      int err;
+
+      err = pthread_mutexattr_init (&attributes);
+      if (err != 0)
+        return thrd_error;
+      err = pthread_mutexattr_settype (&attributes, PTHREAD_MUTEX_RECURSIVE);
+      if (err != 0)
+        {
+          pthread_mutexattr_destroy (&attributes);
+          return thrd_error;
+        }
+      err = pthread_mutex_init (mutex, &attributes);
+      if (err != 0)
+        {
+          pthread_mutexattr_destroy (&attributes);
+          return thrd_error;
+        }
+      err = pthread_mutexattr_destroy (&attributes);
+      if (err != 0)
+        return thrd_error;
+    }
+  else
+    {
+      int err = pthread_mutex_init (mutex, NULL);
+      if (err != 0)
+        return thrd_error;
+    }
+  return thrd_success;
+}
+
+int
+mtx_lock (mtx_t *mutex)
+{
+  int err = pthread_mutex_lock (mutex);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+mtx_trylock (mtx_t *mutex)
+{
+  int err = pthread_mutex_trylock (mutex);
+  return (err == 0 ? thrd_success : err == EBUSY ? thrd_busy : thrd_error);
+}
+
+int
+mtx_timedlock (mtx_t *mutex, const struct timespec *abstime)
+{
+  int err = pthread_mutex_timedlock (mutex, abstime);
+  return (err == 0 ? thrd_success :
+          err == ETIMEDOUT ? thrd_timedout :
+          thrd_error);
+}
+
+int
+mtx_unlock (mtx_t *mutex)
+{
+  int err = pthread_mutex_unlock (mutex);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void
+mtx_destroy (mtx_t *mutex)
+{
+  pthread_mutex_destroy (mutex);
+}
+
+void
+call_once (once_flag *flagp, void (*func) (void))
+{
+  pthread_once (flagp, func);
+}
+
+#endif
diff --git a/modules/mtx b/modules/mtx
new file mode 100644
index 0000000..a1d2a8a
--- /dev/null
+++ b/modules/mtx
@@ -0,0 +1,34 @@
+Description:
+ISO C 11 mutex functions.
+
+Files:
+lib/mtx.c
+
+Depends-on:
+threads-h
+pthread_mutex_timedlock
+windows-mutex
+windows-recmutex
+windows-timedmutex
+windows-timedrecmutex
+
+configure.ac:
+AC_REQUIRE([gl_THREADS_H])
+if test $HAVE_THREADS_H = 0; then
+  AC_LIBOBJ([mtx])
+fi
+gl_THREADS_MODULE_INDICATOR([mtx])
+
+Makefile.am:
+
+Include:
+<threads.h>
+
+Link:
+$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #6: 0020-cnd-New-module.patch --]
[-- Type: text/x-patch, Size: 13271 bytes --]

From 36307d84f7bed226e8059c61ea611cf01b1dfdc4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:32:42 +0200
Subject: [PATCH 20/26] cnd: New module.

* lib/cnd.c: New file.
* modules/cnd: New file.
* doc/posix-functions/cnd_init.texi: Mention the new module.
* doc/posix-functions/cnd_wait.texi: Likewise.
* doc/posix-functions/cnd_timedwait.texi: Likewise.
* doc/posix-functions/cnd_signal.texi: Likewise.
* doc/posix-functions/cnd_broadcast.texi: Likewise.
* doc/posix-functions/cnd_destroy.texi: Likewise.
---
 ChangeLog                              |  12 +++
 doc/posix-functions/cnd_broadcast.texi |   8 +-
 doc/posix-functions/cnd_destroy.texi   |   8 +-
 doc/posix-functions/cnd_init.texi      |   8 +-
 doc/posix-functions/cnd_signal.texi    |   8 +-
 doc/posix-functions/cnd_timedwait.texi |   8 +-
 doc/posix-functions/cnd_wait.texi      |   8 +-
 lib/cnd.c                              | 185 +++++++++++++++++++++++++++++++++
 modules/cnd                            |  31 ++++++
 9 files changed, 252 insertions(+), 24 deletions(-)
 create mode 100644 lib/cnd.c
 create mode 100644 modules/cnd

diff --git a/ChangeLog b/ChangeLog
index 0e73bd6..68eeea7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	cnd: New module.
+	* lib/cnd.c: New file.
+	* modules/cnd: New file.
+	* doc/posix-functions/cnd_init.texi: Mention the new module.
+	* doc/posix-functions/cnd_wait.texi: Likewise.
+	* doc/posix-functions/cnd_timedwait.texi: Likewise.
+	* doc/posix-functions/cnd_signal.texi: Likewise.
+	* doc/posix-functions/cnd_broadcast.texi: Likewise.
+	* doc/posix-functions/cnd_destroy.texi: Likewise.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	mtx: New module.
 	* lib/mtx.c: New file.
 	* modules/mtx: New file.
diff --git a/doc/posix-functions/cnd_broadcast.texi b/doc/posix-functions/cnd_broadcast.texi
index a11d52d..e44a908 100644
--- a/doc/posix-functions/cnd_broadcast.texi
+++ b/doc/posix-functions/cnd_broadcast.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/cnd_destroy.texi b/doc/posix-functions/cnd_destroy.texi
index 1ae39fd..13783a3 100644
--- a/doc/posix-functions/cnd_destroy.texi
+++ b/doc/posix-functions/cnd_destroy.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/cnd_init.texi b/doc/posix-functions/cnd_init.texi
index 55c7489..a741303 100644
--- a/doc/posix-functions/cnd_init.texi
+++ b/doc/posix-functions/cnd_init.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/cnd_signal.texi b/doc/posix-functions/cnd_signal.texi
index 83071e5..22e2ecc 100644
--- a/doc/posix-functions/cnd_signal.texi
+++ b/doc/posix-functions/cnd_signal.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/cnd_timedwait.texi b/doc/posix-functions/cnd_timedwait.texi
index 3fa6985..0b5f29d 100644
--- a/doc/posix-functions/cnd_timedwait.texi
+++ b/doc/posix-functions/cnd_timedwait.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/cnd_wait.texi b/doc/posix-functions/cnd_wait.texi
index 89e9864..626a6aa 100644
--- a/doc/posix-functions/cnd_wait.texi
+++ b/doc/posix-functions/cnd_wait.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Condition-Variables.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: cnd
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/lib/cnd.c b/lib/cnd.c
new file mode 100644
index 0000000..48c4854
--- /dev/null
+++ b/lib/cnd.c
@@ -0,0 +1,185 @@
+/* ISO C 11 condition variables for multithreading.
+   Copyright (C) 2008-2019 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Yoann Vandoorselaere <yoann@prelude-ids.org>, 2008,
+   and Bruno Haible <bruno@clisp.org>, 2008, 2019.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include <errno.h>
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+# define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+# include <windows.h>
+
+# include "windows-mutex.h"
+# include "windows-recmutex.h"
+# include "windows-timedmutex.h"
+# include "windows-timedrecmutex.h"
+
+#else
+/* Use POSIX threads.  */
+
+# include <pthread.h>
+
+#endif
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+int
+cnd_init (cnd_t *cond)
+{
+  int err = glwthread_cond_init (cond);
+  return (err == 0 ? thrd_success :
+          err == ENOMEM ? thrd_nomem :
+          thrd_error);
+}
+
+static int
+mutex_lock (mtx_t *mutex)
+{
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      return glwthread_mutex_lock (&mutex->u.u_mutex);
+    case mtx_plain | mtx_recursive:
+      return glwthread_recmutex_lock (&mutex->u.u_recmutex);
+    case mtx_timed:
+      return glwthread_timedmutex_lock (&mutex->u.u_timedmutex);
+    case mtx_timed | mtx_recursive:
+      return glwthread_timedrecmutex_lock (&mutex->u.u_timedrecmutex);
+    default:
+      abort ();
+    }
+}
+
+static int
+mutex_unlock (mtx_t *mutex)
+{
+  switch (mutex->type)
+    {
+    case mtx_plain:
+      return glwthread_mutex_unlock (&mutex->u.u_mutex);
+    case mtx_plain | mtx_recursive:
+      return glwthread_recmutex_unlock (&mutex->u.u_recmutex);
+    case mtx_timed:
+      return glwthread_timedmutex_unlock (&mutex->u.u_timedmutex);
+    case mtx_timed | mtx_recursive:
+      return glwthread_timedrecmutex_unlock (&mutex->u.u_timedrecmutex);
+    default:
+      abort ();
+    }
+}
+
+int
+cnd_wait (cnd_t *cond, mtx_t *mutex)
+{
+  int err = glwthread_cond_wait (cond, mutex,
+                                 (int (*) (void *)) mutex_lock,
+                                 (int (*) (void *)) mutex_unlock);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+cnd_timedwait (cnd_t *cond, mtx_t *mutex, const struct timespec *abstime)
+{
+  int err = glwthread_cond_timedwait (cond, mutex,
+                                      (int (*) (void *)) mutex_lock,
+                                      (int (*) (void *)) mutex_unlock,
+                                      abstime);
+  return (err == 0 ? thrd_success :
+          err == ETIMEDOUT ? thrd_timedout :
+          thrd_error);
+}
+
+int
+cnd_signal (cnd_t *cond)
+{
+  int err = glwthread_cond_signal (cond);
+  return (err == 0 ? thrd_success :
+          err == ENOMEM ? thrd_nomem :
+          thrd_error);
+}
+
+int
+cnd_broadcast (cnd_t *cond)
+{
+  int err = glwthread_cond_broadcast (cond);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void
+cnd_destroy (cnd_t *cond)
+{
+  glwthread_cond_destroy (cond);
+}
+
+#else
+/* Use POSIX threads.  */
+
+int
+cnd_init (cnd_t *cond)
+{
+  int err = pthread_cond_init (cond, NULL);
+  return (err == 0 ? thrd_success :
+          err == ENOMEM ? thrd_nomem :
+          thrd_error);
+}
+
+int
+cnd_wait (cnd_t *cond, mtx_t *mutex)
+{
+  int err = pthread_cond_wait (cond, mutex);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+int
+cnd_timedwait (cnd_t *cond, mtx_t *mutex, const struct timespec *abstime)
+{
+  int err = pthread_cond_timedwait (cond, mutex, abstime);
+  return (err == 0 ? thrd_success :
+          err == ETIMEDOUT ? thrd_timedout :
+          thrd_error);
+}
+
+int
+cnd_signal (cnd_t *cond)
+{
+  int err = pthread_cond_signal (cond);
+  return (err == 0 ? thrd_success :
+          err == ENOMEM ? thrd_nomem :
+          thrd_error);
+}
+
+int
+cnd_broadcast (cnd_t *cond)
+{
+  int err = pthread_cond_broadcast (cond);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void
+cnd_destroy (cnd_t *cond)
+{
+  pthread_cond_destroy (cond);
+}
+
+#endif
diff --git a/modules/cnd b/modules/cnd
new file mode 100644
index 0000000..635a4224
--- /dev/null
+++ b/modules/cnd
@@ -0,0 +1,31 @@
+Description:
+ISO C 11 condition variable functions.
+
+Files:
+lib/cnd.c
+
+Depends-on:
+threads-h
+windows-cond
+gettimeofday
+
+configure.ac:
+AC_REQUIRE([gl_THREADS_H])
+if test $HAVE_THREADS_H = 0; then
+  AC_LIBOBJ([cnd])
+fi
+gl_THREADS_MODULE_INDICATOR([cnd])
+
+Makefile.am:
+
+Include:
+<threads.h>
+
+Link:
+$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #7: 0021-tss-New-module.patch --]
[-- Type: text/x-patch, Size: 8560 bytes --]

From ee2f4139e12114621dc1e4db54c0b1658a9fadac Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:34:49 +0200
Subject: [PATCH 21/26] tss: New module.

* lib/tss.c: New file.
* modules/tss: New file.
* doc/posix-functions/tss_create.texi: Mention the new module.
* doc/posix-functions/tss_set.texi: Likewise.
* doc/posix-functions/tss_get.texi: Likewise.
* doc/posix-functions/tss_delete.texi: Likewise.
---
 ChangeLog                           |  10 ++++
 doc/posix-functions/tss_create.texi |   8 +--
 doc/posix-functions/tss_delete.texi |   8 +--
 doc/posix-functions/tss_get.texi    |   8 +--
 doc/posix-functions/tss_set.texi    |   8 +--
 lib/tss.c                           | 108 ++++++++++++++++++++++++++++++++++++
 modules/tss                         |  30 ++++++++++
 7 files changed, 164 insertions(+), 16 deletions(-)
 create mode 100644 lib/tss.c
 create mode 100644 modules/tss

diff --git a/ChangeLog b/ChangeLog
index 68eeea7..4c75b69 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	tss: New module.
+	* lib/tss.c: New file.
+	* modules/tss: New file.
+	* doc/posix-functions/tss_create.texi: Mention the new module.
+	* doc/posix-functions/tss_set.texi: Likewise.
+	* doc/posix-functions/tss_get.texi: Likewise.
+	* doc/posix-functions/tss_delete.texi: Likewise.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	cnd: New module.
 	* lib/cnd.c: New file.
 	* modules/cnd: New file.
diff --git a/doc/posix-functions/tss_create.texi b/doc/posix-functions/tss_create.texi
index 833cd87..86fab4e 100644
--- a/doc/posix-functions/tss_create.texi
+++ b/doc/posix-functions/tss_create.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread_002dlocal-Storage.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: tss
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/tss_delete.texi b/doc/posix-functions/tss_delete.texi
index c122828..8527324 100644
--- a/doc/posix-functions/tss_delete.texi
+++ b/doc/posix-functions/tss_delete.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread_002dlocal-Storage.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: tss
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/tss_get.texi b/doc/posix-functions/tss_get.texi
index 0cc14d8..b8109cb 100644
--- a/doc/posix-functions/tss_get.texi
+++ b/doc/posix-functions/tss_get.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread_002dlocal-Storage.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: tss
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/doc/posix-functions/tss_set.texi b/doc/posix-functions/tss_set.texi
index 533ad89..d0091f0 100644
--- a/doc/posix-functions/tss_set.texi
+++ b/doc/posix-functions/tss_set.texi
@@ -10,15 +10,15 @@ Documentation:@*
 @url{https://www.gnu.org/software/libc/manual/html_node/ISO-C-Thread_002dlocal-Storage.html}.
 @end ifnotinfo
 
-Gnulib module: ---
+Gnulib module: tss
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many platforms:
+glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many platforms:
-glibc 2.27, Mac OS X 10.5, FreeBSD 9.3, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, OSF/1 5.1, Solaris 11.3, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.
 @end itemize
diff --git a/lib/tss.c b/lib/tss.c
new file mode 100644
index 0000000..d8c0bb5
--- /dev/null
+++ b/lib/tss.c
@@ -0,0 +1,108 @@
+/* ISO C 11 thread-specific storage in multithreaded situations.
+   Copyright (C) 2005-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005, 2019.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include <string.h>
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+# define WIN32_LEAN_AND_MEAN  /* avoid including junk */
+# include <windows.h>
+
+#else
+/* Use POSIX threads.  */
+
+# include <pthread.h>
+
+#endif
+
+#if defined _WIN32 && ! defined __CYGWIN__
+/* Use Windows threads.  */
+
+int
+tss_create (tss_t *keyp, tss_dtor_t destructor)
+{
+  int err = glwthread_tls_key_create (keyp, destructor);
+  if (err == 0)
+    return thrd_success;
+  else
+    {
+      memset (keyp, '\0', sizeof (tss_t));
+      return thrd_error;
+    }
+}
+
+int
+tss_set (tss_t key, void *value)
+{
+  int err = glwthread_tls_set (key, value);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void *
+tss_get (tss_t key)
+{
+  return glwthread_tls_get (key);
+}
+
+void
+tss_delete (tss_t key)
+{
+  glwthread_tls_key_delete (key);
+}
+
+#else
+/* Use POSIX threads.  */
+
+int
+tss_create (tss_t *keyp, tss_dtor_t destructor)
+{
+  int err = pthread_key_create (keyp, destructor);
+  if (err == 0)
+    return thrd_success;
+  else
+    {
+      memset (keyp, '\0', sizeof (tss_t));
+      return thrd_error;
+    }
+}
+
+int
+tss_set (tss_t key, void *value)
+{
+  int err = pthread_setspecific (key, value);
+  return (err == 0 ? thrd_success : thrd_error);
+}
+
+void *
+tss_get (tss_t key)
+{
+  return pthread_getspecific (key);
+}
+
+void
+tss_delete (tss_t key)
+{
+  pthread_key_delete (key);
+}
+
+#endif
diff --git a/modules/tss b/modules/tss
new file mode 100644
index 0000000..4a28200
--- /dev/null
+++ b/modules/tss
@@ -0,0 +1,30 @@
+Description:
+ISO C 11 thread-specific storage functions.
+
+Files:
+lib/tss.c
+
+Depends-on:
+threads-h
+windows-tls
+
+configure.ac:
+AC_REQUIRE([gl_THREADS_H])
+if test $HAVE_THREADS_H = 0; then
+  AC_LIBOBJ([tss])
+fi
+gl_THREADS_MODULE_INDICATOR([tss])
+
+Makefile.am:
+
+Include:
+<threads.h>
+
+Link:
+$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


[-- Attachment #8: 0022-thrd-Add-tests.patch --]
[-- Type: text/x-patch, Size: 5034 bytes --]

From 6ac18d06d0234f85c41af69fb6319592e2d4f5c6 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:37:23 +0200
Subject: [PATCH 22/26] thrd: Add tests.

* tests/test-thrd_create.c: New file, based on
tests/test-thread_create.c.
* tests/test-thrd_current.c: New file, based on
tests/test-thread_self.c.
* modules/thrd-tests: New file.
---
 ChangeLog                 |  9 ++++++
 modules/thrd-tests        | 14 +++++++++
 tests/test-thrd_create.c  | 73 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-thrd_current.c | 31 ++++++++++++++++++++
 4 files changed, 127 insertions(+)
 create mode 100644 modules/thrd-tests
 create mode 100644 tests/test-thrd_create.c
 create mode 100644 tests/test-thrd_current.c

diff --git a/ChangeLog b/ChangeLog
index 4c75b69..b17e751 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	thrd: Add tests.
+	* tests/test-thrd_create.c: New file, based on
+	tests/test-thread_create.c.
+	* tests/test-thrd_current.c: New file, based on
+	tests/test-thread_self.c.
+	* modules/thrd-tests: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	tss: New module.
 	* lib/tss.c: New file.
 	* modules/tss: New file.
diff --git a/modules/thrd-tests b/modules/thrd-tests
new file mode 100644
index 0000000..fdb6600
--- /dev/null
+++ b/modules/thrd-tests
@@ -0,0 +1,14 @@
+Files:
+tests/test-thrd_current.c
+tests/test-thrd_create.c
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-thrd_current test-thrd_create
+check_PROGRAMS += test-thrd_current test-thrd_create
+test_thrd_current_LDADD = $(LDADD) @LIBSTDTHREAD@
+test_thrd_create_LDADD = $(LDADD) @LIBSTDTHREAD@
diff --git a/tests/test-thrd_create.c b/tests/test-thrd_create.c
new file mode 100644
index 0000000..af1eb53
--- /dev/null
+++ b/tests/test-thrd_create.c
@@ -0,0 +1,73 @@
+/* Test of thrd_create () macro.
+   Copyright (C) 2011-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2011.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "macros.h"
+
+static thrd_t main_thread_before;
+static thrd_t main_thread_after;
+static thrd_t worker_thread;
+
+#define MAGIC 1266074729
+static volatile int work_done;
+
+static int
+worker_thread_func (void *arg)
+{
+  work_done = 1;
+  return MAGIC;
+}
+
+int
+main ()
+{
+  main_thread_before = thrd_current ();
+
+  if (thrd_create (&worker_thread, worker_thread_func, NULL) == thrd_success)
+    {
+      int ret;
+
+      /* Check that thrd_current () has the same value before than after the
+         first call to thrd_create ().  */
+      main_thread_after = thrd_current ();
+      ASSERT (memcmp (&main_thread_before, &main_thread_after,
+                      sizeof (thrd_t))
+              == 0);
+
+      ASSERT (thrd_join (worker_thread, &ret) == thrd_success);
+
+      /* Check the return value of the thread.  */
+      ASSERT (ret == MAGIC);
+
+      /* Check that worker_thread_func () has finished executing.  */
+      ASSERT (work_done);
+
+      return 0;
+    }
+  else
+    {
+      fputs ("thrd_create failed\n", stderr);
+      return 1;
+    }
+}
diff --git a/tests/test-thrd_current.c b/tests/test-thrd_current.c
new file mode 100644
index 0000000..9bb5258
--- /dev/null
+++ b/tests/test-thrd_current.c
@@ -0,0 +1,31 @@
+/* Test of thrd_current () function.
+   Copyright (C) 2011-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2011.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+thrd_t main_thread;
+
+int
+main ()
+{
+  main_thread = thrd_current ();
+
+  return 0;
+}
-- 
2.7.4


[-- Attachment #9: 0023-mtx-Add-tests.patch --]
[-- Type: text/x-patch, Size: 21868 bytes --]

From 4a440ef9b6c2568f54b0487b1ebaffc597efb399 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:38:06 +0200
Subject: [PATCH 23/26] mtx: Add tests.

* tests/test-mtx.c: New file, based on tests/test-lock.c.
* tests/test-call_once.c: New file, based on tests/test-once.c.
* modules/mtx-tests: New file.
---
 ChangeLog              |   7 +
 modules/mtx-tests      |  19 ++
 tests/test-call_once.c |  43 ++++
 tests/test-mtx.c       | 649 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 718 insertions(+)
 create mode 100644 modules/mtx-tests
 create mode 100644 tests/test-call_once.c
 create mode 100644 tests/test-mtx.c

diff --git a/ChangeLog b/ChangeLog
index b17e751..1f658d1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	mtx: Add tests.
+	* tests/test-mtx.c: New file, based on tests/test-lock.c.
+	* tests/test-call_once.c: New file, based on tests/test-once.c.
+	* modules/mtx-tests: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	thrd: Add tests.
 	* tests/test-thrd_create.c: New file, based on
 	tests/test-thread_create.c.
diff --git a/modules/mtx-tests b/modules/mtx-tests
new file mode 100644
index 0000000..7714ebc
--- /dev/null
+++ b/modules/mtx-tests
@@ -0,0 +1,19 @@
+Files:
+tests/test-mtx.c
+tests/test-call_once.c
+tests/macros.h
+
+Depends-on:
+thrd
+lock
+stdint
+
+configure.ac:
+AC_CHECK_HEADERS_ONCE([semaphore.h])
+AC_CHECK_DECLS_ONCE([alarm])
+
+Makefile.am:
+TESTS += test-mtx test-call_once
+check_PROGRAMS += test-mtx test-call_once
+test_mtx_LDADD = $(LDADD) @LIBSTDTHREAD@ @LIBTHREAD@
+test_call_once_LDADD = $(LDADD) @LIBSTDTHREAD@
diff --git a/tests/test-call_once.c b/tests/test-call_once.c
new file mode 100644
index 0000000..8d2b785
--- /dev/null
+++ b/tests/test-call_once.c
@@ -0,0 +1,43 @@
+/* Test of once-only execution in multithreaded situations.
+   Copyright (C) 2018-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2018.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#include "macros.h"
+
+static once_flag a_once = ONCE_FLAG_INIT;
+
+static int a;
+
+static void
+a_init (void)
+{
+  a = 42;
+}
+
+int
+main ()
+{
+  call_once (&a_once, a_init);
+
+  ASSERT (a == 42);
+
+  return 0;
+}
diff --git a/tests/test-mtx.c b/tests/test-mtx.c
new file mode 100644
index 0000000..0d74492
--- /dev/null
+++ b/tests/test-mtx.c
@@ -0,0 +1,649 @@
+/* Test of locking in multithreaded situations.
+   Copyright (C) 2005, 2008-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#include <config.h>
+
+/* 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_LOCK 1
+#define DO_TEST_RECURSIVE_LOCK 1
+#define DO_TEST_ONCE 1
+
+/* Whether to help the scheduler through explicit thrd_yield().
+   Uncomment this to see if the operating system has a fair scheduler.  */
+#define EXPLICIT_YIELD 1
+
+/* Whether to use 'volatile' on some variables that communicate information
+   between threads.  If set to 0, a semaphore or a lock is used to protect
+   these variables.  If set to 1, 'volatile' is used; this is theoretically
+   equivalent but can lead to much slower execution (e.g. 30x slower total
+   run time on a 40-core machine), because 'volatile' does not imply any
+   synchronization/communication between different CPUs.  */
+#define USE_VOLATILE 0
+
+#if USE_POSIX_THREADS && HAVE_SEMAPHORE_H
+/* Whether to use a semaphore to communicate information between threads.
+   If set to 0, a lock is used. If set to 1, a semaphore is used.
+   Uncomment this to reduce the dependencies of this test.  */
+# define USE_SEMAPHORE 1
+/* Mac OS X provides only named semaphores (sem_open); its facility for
+   unnamed semaphores (sem_init) does not work.  */
+# if defined __APPLE__ && defined __MACH__
+#  define USE_NAMED_SEMAPHORE 1
+# else
+#  define USE_UNNAMED_SEMAPHORE 1
+# endif
+#endif
+
+/* Whether to print debugging messages.  */
+#define ENABLE_DEBUGGING 0
+
+/* Number of simultaneous threads.  */
+#define THREAD_COUNT 10
+
+/* Number of operations performed in each thread.
+   This is quite high, because with a smaller count, say 5000, we often get
+   an "OK" result even without ENABLE_LOCKING (on Linux/x86).  */
+#define REPEAT_COUNT 50000
+
+#include <threads.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "glthread/lock.h"
+
+#if USE_SEMAPHORE
+# include <errno.h>
+# include <fcntl.h>
+# include <semaphore.h>
+# include <unistd.h>
+#endif
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "macros.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() thrd_yield ()
+#else
+# define yield()
+#endif
+
+#if USE_VOLATILE
+struct atomic_int {
+  volatile int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+  return ai->value;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+  ai->value = new_value;
+}
+#elif USE_SEMAPHORE
+/* This atomic_int implementation can only support the values 0 and 1.
+   It is initially 0 and can be set to 1 only once.  */
+# if USE_UNNAMED_SEMAPHORE
+struct atomic_int {
+  sem_t semaphore;
+};
+#define atomic_int_semaphore(ai) (&(ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+  sem_init (&ai->semaphore, 0, 0);
+}
+# endif
+# if USE_NAMED_SEMAPHORE
+struct atomic_int {
+  sem_t *semaphore;
+};
+#define atomic_int_semaphore(ai) ((ai)->semaphore)
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+  sem_t *s;
+  unsigned int count;
+  for (count = 0; ; count++)
+    {
+      char name[80];
+      /* Use getpid() in the name, so that different processes running at the
+         same time will not interfere.  Use ai in the name, so that different
+         atomic_int in the same process will not interfere.  Use a count in
+         the name, so that even in the (unlikely) case that a semaphore with
+         the specified name already exists, we can try a different name.  */
+      sprintf (name, "test-lock-%lu-%p-%u",
+               (unsigned long) getpid (), ai, count);
+      s = sem_open (name, O_CREAT | O_EXCL, 0600, 0);
+      if (s == SEM_FAILED)
+        {
+          if (errno == EEXIST)
+            /* Retry with a different name.  */
+            continue;
+          else
+            {
+              perror ("sem_open failed");
+              abort ();
+            }
+        }
+      else
+        {
+          /* Try not to leave a semaphore hanging around on the file system
+             eternally, if we can avoid it.  */
+          sem_unlink (name);
+          break;
+        }
+    }
+  ai->semaphore = s;
+}
+# endif
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+  if (sem_trywait (atomic_int_semaphore (ai)) == 0)
+    {
+      if (sem_post (atomic_int_semaphore (ai)))
+        abort ();
+      return 1;
+    }
+  else if (errno == EAGAIN)
+    return 0;
+  else
+    abort ();
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+  if (new_value == 0)
+    /* It's already initialized with 0.  */
+    return;
+  /* To set the value 1: */
+  if (sem_post (atomic_int_semaphore (ai)))
+    abort ();
+}
+#else
+struct atomic_int {
+  mtx_t lock;
+  int value;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+  ASSERT (mtx_init (&ai->lock, mtx_plain) == thrd_success);
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+  ASSERT (mtx_lock (&ai->lock) == thrd_success);
+  int ret = ai->value;
+  ASSERT (mtx_unlock (&ai->lock) == thrd_success);
+  return ret;
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+  ASSERT (mtx_lock (&ai->lock) == thrd_success);
+  ai->value = new_value;
+  ASSERT (mtx_unlock (&ai->lock) == thrd_success);
+}
+#endif
+
+/* Returns a reference to the current thread as a pointer, for debugging.  */
+#if defined __MVS__
+  /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
+     The first three bytes of this field appear to uniquely identify a
+     pthread_t, though not necessarily representing a pointer.  */
+# define thrd_current_pointer() (*((void **) thrd_current ().__))
+#elif defined __sun
+  /* On Solaris, thrd_t is merely an 'unsigned int'.  */
+# define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
+#else
+# define thrd_current_pointer() ((void *) thrd_current ())
+#endif
+
+#define ACCOUNT_COUNT 4
+
+static int account[ACCOUNT_COUNT];
+
+static int
+random_account (void)
+{
+  return ((unsigned int) rand () >> 3) % ACCOUNT_COUNT;
+}
+
+static void
+check_accounts (void)
+{
+  int i, sum;
+
+  sum = 0;
+  for (i = 0; i < ACCOUNT_COUNT; i++)
+    sum += account[i];
+  if (sum != ACCOUNT_COUNT * 1000)
+    abort ();
+}
+
+
+/* ------------------- Test normal (non-recursive) locks ------------------- */
+
+/* Test normal locks by having several bank accounts and several threads
+   which shuffle around money between the accounts and another thread
+   checking that all the money is still there.  */
+
+static mtx_t my_lock;
+
+static int
+lock_mutator_thread (void *arg)
+{
+  int repeat;
+
+  for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+    {
+      int i1, i2, value;
+
+      dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
+      ASSERT (mtx_lock (&my_lock) == thrd_success);
+      dbgprintf ("Mutator %p after  lock\n", thrd_current_pointer ());
+
+      i1 = random_account ();
+      i2 = random_account ();
+      value = ((unsigned int) rand () >> 3) % 10;
+      account[i1] += value;
+      account[i2] -= value;
+
+      dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
+      ASSERT (mtx_unlock (&my_lock) == thrd_success);
+      dbgprintf ("Mutator %p after  unlock\n", thrd_current_pointer ());
+
+      dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
+      ASSERT (mtx_lock (&my_lock) == thrd_success);
+      check_accounts ();
+      ASSERT (mtx_unlock (&my_lock) == thrd_success);
+      dbgprintf ("Mutator %p after  check unlock\n", thrd_current_pointer ());
+
+      yield ();
+    }
+
+  dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static struct atomic_int lock_checker_done;
+
+static int
+lock_checker_thread (void *arg)
+{
+  while (get_atomic_int_value (&lock_checker_done) == 0)
+    {
+      dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
+      ASSERT (mtx_lock (&my_lock) == thrd_success);
+      check_accounts ();
+      ASSERT (mtx_unlock (&my_lock) == thrd_success);
+      dbgprintf ("Checker %p after  check unlock\n", thrd_current_pointer ());
+
+      yield ();
+    }
+
+  dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static void
+test_mtx_plain (void)
+{
+  int i;
+  thrd_t checkerthread;
+  thrd_t threads[THREAD_COUNT];
+
+  /* Initialization.  */
+  for (i = 0; i < ACCOUNT_COUNT; i++)
+    account[i] = 1000;
+  init_atomic_int (&lock_checker_done);
+  set_atomic_int_value (&lock_checker_done, 0);
+
+  /* Spawn the threads.  */
+  ASSERT (thrd_create (&checkerthread, lock_checker_thread, NULL)
+          == thrd_success);
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_create (&threads[i], lock_mutator_thread, NULL)
+            == thrd_success);
+
+  /* Wait for the threads to terminate.  */
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_join (threads[i], NULL) == thrd_success);
+  set_atomic_int_value (&lock_checker_done, 1);
+  ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
+  check_accounts ();
+}
+
+
+/* -------------------------- Test recursive locks -------------------------- */
+
+/* Test recursive locks by having several bank accounts and several threads
+   which shuffle around money between the accounts (recursively) and another
+   thread checking that all the money is still there.  */
+
+static mtx_t my_reclock;
+
+static void
+recshuffle (void)
+{
+  int i1, i2, value;
+
+  dbgprintf ("Mutator %p before lock\n", thrd_current_pointer ());
+  ASSERT (mtx_lock (&my_reclock) == thrd_success);
+  dbgprintf ("Mutator %p after  lock\n", thrd_current_pointer ());
+
+  i1 = random_account ();
+  i2 = random_account ();
+  value = ((unsigned int) rand () >> 3) % 10;
+  account[i1] += value;
+  account[i2] -= value;
+
+  /* Recursive with probability 0.5.  */
+  if (((unsigned int) rand () >> 3) % 2)
+    recshuffle ();
+
+  dbgprintf ("Mutator %p before unlock\n", thrd_current_pointer ());
+  ASSERT (mtx_unlock (&my_reclock) == thrd_success);
+  dbgprintf ("Mutator %p after  unlock\n", thrd_current_pointer ());
+}
+
+static int
+reclock_mutator_thread (void *arg)
+{
+  int repeat;
+
+  for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+    {
+      recshuffle ();
+
+      dbgprintf ("Mutator %p before check lock\n", thrd_current_pointer ());
+      ASSERT (mtx_lock (&my_reclock) == thrd_success);
+      check_accounts ();
+      ASSERT (mtx_unlock (&my_reclock) == thrd_success);
+      dbgprintf ("Mutator %p after  check unlock\n", thrd_current_pointer ());
+
+      yield ();
+    }
+
+  dbgprintf ("Mutator %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static struct atomic_int reclock_checker_done;
+
+static int
+reclock_checker_thread (void *arg)
+{
+  while (get_atomic_int_value (&reclock_checker_done) == 0)
+    {
+      dbgprintf ("Checker %p before check lock\n", thrd_current_pointer ());
+      ASSERT (mtx_lock (&my_reclock) == thrd_success);
+      check_accounts ();
+      ASSERT (mtx_unlock (&my_reclock) == thrd_success);
+      dbgprintf ("Checker %p after  check unlock\n", thrd_current_pointer ());
+
+      yield ();
+    }
+
+  dbgprintf ("Checker %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static void
+test_mtx_recursive (void)
+{
+  int i;
+  thrd_t checkerthread;
+  thrd_t threads[THREAD_COUNT];
+
+  /* Initialization.  */
+  for (i = 0; i < ACCOUNT_COUNT; i++)
+    account[i] = 1000;
+  init_atomic_int (&reclock_checker_done);
+  set_atomic_int_value (&reclock_checker_done, 0);
+
+  /* Spawn the threads.  */
+  ASSERT (thrd_create (&checkerthread, reclock_checker_thread, NULL)
+          == thrd_success);
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_create (&threads[i], reclock_mutator_thread, NULL)
+            == thrd_success);
+
+  /* Wait for the threads to terminate.  */
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_join (threads[i], NULL) == thrd_success);
+  set_atomic_int_value (&reclock_checker_done, 1);
+  ASSERT (thrd_join (checkerthread, NULL) == thrd_success);
+  check_accounts ();
+}
+
+
+/* ------------------------ Test once-only execution ------------------------ */
+
+/* Test once-only execution by having several threads attempt to grab a
+   once-only task simultaneously (triggered by releasing a read-write lock).  */
+
+static once_flag fresh_once = ONCE_FLAG_INIT;
+static int ready[THREAD_COUNT];
+static mtx_t ready_lock[THREAD_COUNT];
+#if ENABLE_LOCKING
+static gl_rwlock_t fire_signal[REPEAT_COUNT];
+#else
+static volatile int fire_signal_state;
+#endif
+static once_flag once_control;
+static int performed;
+static mtx_t performed_lock;
+
+static void
+once_execute (void)
+{
+  ASSERT (mtx_lock (&performed_lock) == thrd_success);
+  performed++;
+  ASSERT (mtx_unlock (&performed_lock) == thrd_success);
+}
+
+static int
+once_contender_thread (void *arg)
+{
+  int id = (int) (intptr_t) arg;
+  int repeat;
+
+  for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
+    {
+      /* Tell the main thread that we're ready.  */
+      ASSERT (mtx_lock (&ready_lock[id]) == thrd_success);
+      ready[id] = 1;
+      ASSERT (mtx_unlock (&ready_lock[id]) == thrd_success);
+
+      if (repeat == REPEAT_COUNT)
+        break;
+
+      dbgprintf ("Contender %p waiting for signal for round %d\n",
+                 thrd_current_pointer (), repeat);
+#if ENABLE_LOCKING
+      /* Wait for the signal to go.  */
+      gl_rwlock_rdlock (fire_signal[repeat]);
+      /* And don't hinder the others (if the scheduler is unfair).  */
+      gl_rwlock_unlock (fire_signal[repeat]);
+#else
+      /* Wait for the signal to go.  */
+      while (fire_signal_state <= repeat)
+        yield ();
+#endif
+      dbgprintf ("Contender %p got the     signal for round %d\n",
+                 thrd_current_pointer (), repeat);
+
+      /* Contend for execution.  */
+      call_once (&once_control, once_execute);
+    }
+
+  return 0;
+}
+
+static void
+test_once (void)
+{
+  int i, repeat;
+  thrd_t threads[THREAD_COUNT];
+
+  /* Initialize all variables.  */
+  for (i = 0; i < THREAD_COUNT; i++)
+    {
+      ready[i] = 0;
+      ASSERT (mtx_init (&ready_lock[i], mtx_plain) == thrd_success);
+    }
+#if ENABLE_LOCKING
+  for (i = 0; i < REPEAT_COUNT; i++)
+    gl_rwlock_init (fire_signal[i]);
+#else
+  fire_signal_state = 0;
+#endif
+
+#if ENABLE_LOCKING
+  /* Block all fire_signals.  */
+  for (i = REPEAT_COUNT-1; i >= 0; i--)
+    gl_rwlock_wrlock (fire_signal[i]);
+#endif
+
+  /* Spawn the threads.  */
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_create (&threads[i],
+                         once_contender_thread, (void *) (intptr_t) i)
+            == thrd_success);
+
+  for (repeat = 0; repeat <= REPEAT_COUNT; repeat++)
+    {
+      /* Wait until every thread is ready.  */
+      dbgprintf ("Main thread before synchronizing for round %d\n", repeat);
+      for (;;)
+        {
+          int ready_count = 0;
+          for (i = 0; i < THREAD_COUNT; i++)
+            {
+              ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
+              ready_count += ready[i];
+              ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
+            }
+          if (ready_count == THREAD_COUNT)
+            break;
+          yield ();
+        }
+      dbgprintf ("Main thread after  synchronizing for round %d\n", repeat);
+
+      if (repeat > 0)
+        {
+          /* Check that exactly one thread executed the once_execute()
+             function.  */
+          if (performed != 1)
+            abort ();
+        }
+
+      if (repeat == REPEAT_COUNT)
+        break;
+
+      /* Preparation for the next round: Initialize once_control.  */
+      memcpy (&once_control, &fresh_once, sizeof (once_flag));
+
+      /* Preparation for the next round: Reset the performed counter.  */
+      performed = 0;
+
+      /* Preparation for the next round: Reset the ready flags.  */
+      for (i = 0; i < THREAD_COUNT; i++)
+        {
+          ASSERT (mtx_lock (&ready_lock[i]) == thrd_success);
+          ready[i] = 0;
+          ASSERT (mtx_unlock (&ready_lock[i]) == thrd_success);
+        }
+
+      /* Signal all threads simultaneously.  */
+      dbgprintf ("Main thread giving signal for round %d\n", repeat);
+#if ENABLE_LOCKING
+      gl_rwlock_unlock (fire_signal[repeat]);
+#else
+      fire_signal_state = repeat + 1;
+#endif
+    }
+
+  /* Wait for the threads to terminate.  */
+  for (i = 0; i < THREAD_COUNT; i++)
+    ASSERT (thrd_join (threads[i], NULL) == thrd_success);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+  /* Declare failure if test takes too long, by using default abort
+     caused by SIGALRM.  */
+  int alarm_value = 600;
+  signal (SIGALRM, SIG_DFL);
+  alarm (alarm_value);
+#endif
+
+  ASSERT (mtx_init (&my_lock, mtx_plain) == thrd_success);
+  ASSERT (mtx_init (&my_reclock, mtx_plain | mtx_recursive) == thrd_success);
+  ASSERT (mtx_init (&performed_lock, mtx_plain) == thrd_success);
+
+#if DO_TEST_LOCK
+  printf ("Starting test_mtx_plain ..."); fflush (stdout);
+  test_mtx_plain ();
+  printf (" OK\n"); fflush (stdout);
+#endif
+#if DO_TEST_RECURSIVE_LOCK
+  printf ("Starting test_mtx_recursive ..."); fflush (stdout);
+  test_mtx_recursive ();
+  printf (" OK\n"); fflush (stdout);
+#endif
+#if DO_TEST_ONCE
+  printf ("Starting test_once ..."); fflush (stdout);
+  test_once ();
+  printf (" OK\n"); fflush (stdout);
+#endif
+
+  return 0;
+}
-- 
2.7.4


[-- Attachment #10: 0024-cnd-Add-tests.patch --]
[-- Type: text/x-patch, Size: 6292 bytes --]

From c94b90fcd6f6bdbfff9ccf17bc570065bba51752 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:40:02 +0200
Subject: [PATCH 24/26] cnd: Add tests.

* tests/test-cnd.c: New file, based on tests/test-cond.c.
* modules/cnd-tests: New file.
---
 ChangeLog         |   6 ++
 modules/cnd-tests |  16 +++++
 tests/test-cnd.c  | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 231 insertions(+)
 create mode 100644 modules/cnd-tests
 create mode 100644 tests/test-cnd.c

diff --git a/ChangeLog b/ChangeLog
index 1f658d1..2d3ee5e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	cnd: Add tests.
+	* tests/test-cnd.c: New file, based on tests/test-cond.c.
+	* modules/cnd-tests: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	mtx: Add tests.
 	* tests/test-mtx.c: New file, based on tests/test-lock.c.
 	* tests/test-call_once.c: New file, based on tests/test-once.c.
diff --git a/modules/cnd-tests b/modules/cnd-tests
new file mode 100644
index 0000000..f9dc79e
--- /dev/null
+++ b/modules/cnd-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-cnd.c
+tests/macros.h
+
+Depends-on:
+mtx
+thrd
+gettimeofday
+
+configure.ac:
+AC_CHECK_DECLS_ONCE([alarm])
+
+Makefile.am:
+TESTS += test-cnd
+check_PROGRAMS += test-cnd
+test_cnd_LDADD = $(LDADD) @LIBSTDTHREAD@
diff --git a/tests/test-cnd.c b/tests/test-cnd.c
new file mode 100644
index 0000000..2d8cabc
--- /dev/null
+++ b/tests/test-cnd.c
@@ -0,0 +1,209 @@
+/* Test of condition variables in multithreaded situations.
+   Copyright (C) 2008-2019 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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* 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 <threads.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "macros.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() thrd_yield ()
+#else
+# define yield()
+#endif
+
+
+/*
+ * Condition check
+ */
+static int cond_value = 0;
+static cnd_t condtest;
+static mtx_t lockcond;
+
+static int
+cnd_wait_routine (void *arg)
+{
+  ASSERT (mtx_lock (&lockcond) == thrd_success);
+  while (!cond_value)
+    {
+      ASSERT (cnd_wait (&condtest, &lockcond) == thrd_success);
+    }
+  ASSERT (mtx_unlock (&lockcond) == thrd_success);
+
+  cond_value = 2;
+
+  return 0;
+}
+
+void
+test_cnd_wait ()
+{
+  struct timespec remain;
+  thrd_t thread;
+  int ret;
+
+  remain.tv_sec = 2;
+  remain.tv_nsec = 0;
+
+  cond_value = 0;
+
+  ASSERT (thrd_create (&thread, cnd_wait_routine, NULL) == thrd_success);
+  do
+    {
+      yield ();
+      ret = thrd_sleep (&remain, &remain);
+      ASSERT (ret >= -1);
+    }
+  while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
+
+  /* signal condition */
+  ASSERT (mtx_lock (&lockcond) == thrd_success);
+  cond_value = 1;
+  ASSERT (cnd_signal (&condtest) == thrd_success);
+  ASSERT (mtx_unlock (&lockcond) == thrd_success);
+
+  ASSERT (thrd_join (thread, NULL) == thrd_success);
+
+  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 int
+cnd_timedwait_routine (void *arg)
+{
+  int ret;
+  struct timespec ts;
+
+  ASSERT (mtx_lock (&lockcond) == thrd_success);
+  while (!cond_value)
+    {
+      get_ts (&ts);
+      ret = cnd_timedwait (&condtest, &lockcond, &ts);
+      if (ret == thrd_timedout)
+        cond_timeout = 1;
+    }
+  ASSERT (mtx_unlock (&lockcond) == thrd_success);
+
+  return 0;
+}
+
+static void
+test_cnd_timedwait (void)
+{
+  struct timespec remain;
+  thrd_t thread;
+  int ret;
+
+  remain.tv_sec = 2;
+  remain.tv_nsec = 0;
+
+  cond_value = cond_timeout = 0;
+
+  ASSERT (thrd_create (&thread, cnd_timedwait_routine, NULL) == thrd_success);
+  do
+    {
+      yield ();
+      ret = thrd_sleep (&remain, &remain);
+      ASSERT (ret >= -1);
+    }
+  while (ret == -1 && (remain.tv_sec != 0 || remain.tv_nsec != 0));
+
+  /* signal condition */
+  ASSERT (mtx_lock (&lockcond) == thrd_success);
+  cond_value = 1;
+  ASSERT (cnd_signal (&condtest) == thrd_success);
+  ASSERT (mtx_unlock (&lockcond) == thrd_success);
+
+  ASSERT (thrd_join (thread, NULL) == thrd_success);
+
+  if (!cond_timeout)
+    abort ();
+}
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+  /* Declare failure if test takes too long, by using default abort
+     caused by SIGALRM.  */
+  int alarm_value = 600;
+  signal (SIGALRM, SIG_DFL);
+  alarm (alarm_value);
+#endif
+
+  ASSERT (cnd_init (&condtest) == thrd_success);
+  ASSERT (mtx_init (&lockcond, mtx_plain) == thrd_success);
+
+#if DO_TEST_COND
+  printf ("Starting test_cnd_wait ..."); fflush (stdout);
+  test_cnd_wait ();
+  printf (" OK\n"); fflush (stdout);
+#endif
+#if DO_TEST_TIMEDCOND
+  printf ("Starting test_cnd_timedwait ..."); fflush (stdout);
+  test_cnd_timedwait ();
+  printf (" OK\n"); fflush (stdout);
+#endif
+
+  return 0;
+}
-- 
2.7.4


[-- Attachment #11: 0025-tss-Add-tests.patch --]
[-- Type: text/x-patch, Size: 7651 bytes --]

From 14a5f1378bf1a062cf2163a300cc1991ebe6443f Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:40:39 +0200
Subject: [PATCH 25/26] tss: Add tests.

* tests/test-tss.c: New file, based on tests/test-tls.c.
* modules/tss-tests: New file.
---
 ChangeLog         |   6 ++
 modules/tss-tests |  15 ++++
 tests/test-tss.c  | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 224 insertions(+)
 create mode 100644 modules/tss-tests
 create mode 100644 tests/test-tss.c

diff --git a/ChangeLog b/ChangeLog
index 2d3ee5e..4f1d724 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	tss: Add tests.
+	* tests/test-tss.c: New file, based on tests/test-tls.c.
+	* modules/tss-tests: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	cnd: Add tests.
 	* tests/test-cnd.c: New file, based on tests/test-cond.c.
 	* modules/cnd-tests: New file.
diff --git a/modules/tss-tests b/modules/tss-tests
new file mode 100644
index 0000000..0fbb20e
--- /dev/null
+++ b/modules/tss-tests
@@ -0,0 +1,15 @@
+Files:
+tests/test-tss.c
+tests/macros.h
+
+Depends-on:
+thrd
+stdint
+
+configure.ac:
+AC_CHECK_DECLS_ONCE([alarm])
+
+Makefile.am:
+TESTS += test-tss
+check_PROGRAMS += test-tss
+test_tss_LDADD = $(LDADD) @LIBSTDTHREAD@
diff --git a/tests/test-tss.c b/tests/test-tss.c
new file mode 100644
index 0000000..b23767d
--- /dev/null
+++ b/tests/test-tss.c
@@ -0,0 +1,203 @@
+/* Test of thread-local storage in multithreaded situations.
+   Copyright (C) 2005, 2008-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#include <config.h>
+
+/* 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
+
+/* Number of simultaneous threads.  */
+#define THREAD_COUNT 16
+
+/* Number of operations performed in each thread.  */
+#define REPEAT_COUNT 50000
+
+#include <threads.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "macros.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() thrd_yield ()
+#else
+# define yield()
+#endif
+
+/* Returns a reference to the current thread as a pointer, for debugging.  */
+#if defined __MVS__
+  /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
+     The first three bytes of this field appear to uniquely identify a
+     pthread_t, though not necessarily representing a pointer.  */
+# define thrd_current_pointer() (*((void **) thrd_current ().__))
+#elif defined __sun
+  /* On Solaris, thrd_t is merely an 'unsigned int'.  */
+# define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
+#else
+# define thrd_current_pointer() ((void *) thrd_current ())
+#endif
+
+static void
+perhaps_yield (void)
+{
+  /* Call yield () only with a certain probability, otherwise the
+     sequence of thread activations may be too predictable.  */
+  if ((((unsigned int) rand () >> 3) % 4) == 0)
+    yield ();
+}
+
+
+/* ----------------------- Test thread-local storage ----------------------- */
+
+#define KEYS_COUNT 4
+
+static tss_t mykeys[KEYS_COUNT];
+
+static int
+worker_thread (void *arg)
+{
+  unsigned int id = (unsigned int) (uintptr_t) arg;
+  int i, j, repeat;
+  unsigned int values[KEYS_COUNT];
+
+  dbgprintf ("Worker %p started\n", thrd_current_pointer ());
+
+  /* Initialize the per-thread storage.  */
+  for (i = 0; i < KEYS_COUNT; i++)
+    {
+      values[i] = (((unsigned int) rand () >> 3) % 1000000) * THREAD_COUNT + id;
+      /* Hopefully no arithmetic overflow.  */
+      if ((values[i] % THREAD_COUNT) != id)
+        abort ();
+    }
+  perhaps_yield ();
+
+  /* Verify that the initial value is NULL.  */
+  dbgprintf ("Worker %p before initial verify\n", thrd_current_pointer ());
+  for (i = 0; i < KEYS_COUNT; i++)
+    if (tss_get (mykeys[i]) != NULL)
+      abort ();
+  dbgprintf ("Worker %p after  initial verify\n", thrd_current_pointer ());
+  perhaps_yield ();
+
+  /* Initialize the per-thread storage.  */
+  dbgprintf ("Worker %p before first tls_set\n", thrd_current_pointer ());
+  for (i = 0; i < KEYS_COUNT; i++)
+    {
+      unsigned int *ptr = (unsigned int *) malloc (sizeof (unsigned int));
+      *ptr = values[i];
+      ASSERT (tss_set (mykeys[i], ptr) == thrd_success);
+    }
+  dbgprintf ("Worker %p after  first tls_set\n", thrd_current_pointer ());
+  perhaps_yield ();
+
+  /* Shuffle around the pointers.  */
+  for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+    {
+      dbgprintf ("Worker %p doing value swapping\n", thrd_current_pointer ());
+      i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
+      j = ((unsigned int) rand () >> 3) % KEYS_COUNT;
+      if (i != j)
+        {
+          void *vi = tss_get (mykeys[i]);
+          void *vj = tss_get (mykeys[j]);
+
+          ASSERT (tss_set (mykeys[i], vj) == thrd_success);
+          ASSERT (tss_set (mykeys[j], vi) == thrd_success);
+        }
+      perhaps_yield ();
+    }
+
+  /* Verify that all the values are from this thread.  */
+  dbgprintf ("Worker %p before final verify\n", thrd_current_pointer ());
+  for (i = 0; i < KEYS_COUNT; i++)
+    if ((*(unsigned int *) tss_get (mykeys[i]) % THREAD_COUNT) != id)
+      abort ();
+  dbgprintf ("Worker %p after  final verify\n", thrd_current_pointer ());
+  perhaps_yield ();
+
+  dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static void
+test_tss (void)
+{
+  int pass, i;
+
+  for (pass = 0; pass < 2; pass++)
+    {
+      thrd_t threads[THREAD_COUNT];
+
+      if (pass == 0)
+        for (i = 0; i < KEYS_COUNT; i++)
+          ASSERT (tss_create (&mykeys[i], free) == thrd_success);
+      else
+        for (i = KEYS_COUNT - 1; i >= 0; i--)
+          ASSERT (tss_create (&mykeys[i], free) == thrd_success);
+
+      /* Spawn the threads.  */
+      for (i = 0; i < THREAD_COUNT; i++)
+        ASSERT (thrd_create (&threads[i], worker_thread, NULL) == thrd_success);
+
+      /* Wait for the threads to terminate.  */
+      for (i = 0; i < THREAD_COUNT; i++)
+        ASSERT (thrd_join (threads[i], NULL) == thrd_success);
+
+      for (i = 0; i < KEYS_COUNT; i++)
+        tss_delete (mykeys[i]);
+    }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+  /* Declare failure if test takes too long, by using default abort
+     caused by SIGALRM.  */
+  int alarm_value = 600;
+  signal (SIGALRM, SIG_DFL);
+  alarm (alarm_value);
+#endif
+
+  printf ("Starting test_tss ..."); fflush (stdout);
+  test_tss ();
+  printf (" OK\n"); fflush (stdout);
+
+  return 0;
+}
-- 
2.7.4


[-- Attachment #12: 0026-threads-New-module.patch --]
[-- Type: text/x-patch, Size: 1190 bytes --]

From 0dbc0378bf27549034a43acb123a36e29866e399 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 20 Jun 2019 04:41:03 +0200
Subject: [PATCH 26/26] threads: New module.

* modules/threads: New file.
---
 ChangeLog       |  5 +++++
 modules/threads | 27 +++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 modules/threads

diff --git a/ChangeLog b/ChangeLog
index 4f1d724..0807690 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2019-06-20  Bruno Haible  <bruno@clisp.org>
 
+	threads: New module.
+	* modules/threads: New file.
+
+2019-06-20  Bruno Haible  <bruno@clisp.org>
+
 	tss: Add tests.
 	* tests/test-tss.c: New file, based on tests/test-tls.c.
 	* modules/tss-tests: New file.
diff --git a/modules/threads b/modules/threads
new file mode 100644
index 0000000..2693bd5
--- /dev/null
+++ b/modules/threads
@@ -0,0 +1,27 @@
+Description:
+ISO C 11 multithreading functions.
+
+Files:
+
+Depends-on:
+threads-h
+thrd
+mtx
+cnd
+tss
+
+configure.ac:
+
+Makefile.am:
+
+Include:
+<threads.h>
+
+Link:
+$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
@ 2019-06-20 16:34   ` Paul Eggert
  2019-06-20 17:50     ` Bruno Haible
  2019-06-21  9:22   ` Bruno Haible
  2019-06-21  9:45   ` Bruno Haible
  2 siblings, 1 reply; 11+ messages in thread
From: Paul Eggert @ 2019-06-20 16:34 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib

Thanks for implementing all this. Do you have an example of a package 
that uses this Gnulib module? I'm asking partly because I wonder whether 
coreutils, GNU Emacs, etc. would benefit from it and I'd like to see how 
others use it.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: ISO C 11 threads
  2019-06-20 10:40 ISO C 11 threads Bruno Haible
  2019-06-20 10:42 ` [PATCH] ISO C 11 threads preparations Bruno Haible
  2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
@ 2019-06-20 17:19 ` Bruno Haible
  2019-06-21  1:12 ` Bruno Haible
  3 siblings, 0 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-20 17:19 UTC (permalink / raw)
  To: bug-gnulib

> So far this facility is implemented in
>   - glibc >= 2.29,

Correction: glibc >= 2.28, see [1].

Bruno

[1] https://lists.gnu.org/archive/html/info-gnu/2018-08/msg00000.html



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-06-20 16:34   ` Paul Eggert
@ 2019-06-20 17:50     ` Bruno Haible
  2019-07-01  2:46       ` Bruno Haible
  0 siblings, 1 reply; 11+ messages in thread
From: Bruno Haible @ 2019-06-20 17:50 UTC (permalink / raw)
  To: Paul Eggert; +Cc: bug-gnulib

Hi Paul,

> Thanks for implementing all this. Do you have an example of a package 
> that uses this Gnulib module?

There probably are not many packages that use <threads.h> so far, because
even glibc did not have it until recently (2018-08-01).

And surely no packages use the Gnulib module so far, since I added it just
today :)

> I'm asking partly because I wonder whether coreutils, GNU Emacs, etc.
> would benefit from it 

It depends on whether these packages
  - are compiled and used on native Windows as well,
  - and if so, whether the mingw pthreads are considered unsatisfying
    or whether the use of the MSVC compiler is a requirement.

You can go a long way with <pthread.h>. I expect that <threads.h> will,
in the near future, only be important
  - for portability to Windows,
  - for new operating systems, especially small ones,
  - for education in academics,
  - for implementing C++ threads [1].

> and I'd like to see how others use it.

The use of <threads.h> is very similar to the use of <pthread.h>.

Bruno

[1] https://en.cppreference.com/w/cpp/thread



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: ISO C 11 threads
  2019-06-20 10:40 ISO C 11 threads Bruno Haible
                   ` (2 preceding siblings ...)
  2019-06-20 17:19 ` ISO C 11 threads Bruno Haible
@ 2019-06-21  1:12 ` Bruno Haible
  3 siblings, 0 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-21  1:12 UTC (permalink / raw)
  To: bug-gnulib

I wrote:
> So far this facility is implemented in
>   - glibc >= 2.29,
>   - FreeBSD >= 10,
>   - illumos,
>   - Solaris >= 11.4 (with a bug in thrd_join),
>   - AIX >= 7.1 (with terrible bugs in thrd_create and thrd_join).

Another correction: It's also implemented in musl libc (e.g. Alpine Linux 3.7).
So the list is:
  - glibc >= 2.28,
  - musl libc,
  - FreeBSD >= 10,
  - illumos,
  - Solaris >= 11.4 (with a bug in thrd_join),
  - AIX >= 7.1 (with terrible bugs in thrd_create and thrd_join).

Bruno



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
  2019-06-20 16:34   ` Paul Eggert
@ 2019-06-21  9:22   ` Bruno Haible
  2019-06-21  9:45   ` Bruno Haible
  2 siblings, 0 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-21  9:22 UTC (permalink / raw)
  To: bug-gnulib

A small simplification.


2019-06-20  Bruno Haible  <bruno@clisp.org>

	threads-h: Simplify link dependencies.
	* m4/threads.m4 (gl_THREADS_H): Bail out if Pth threading is requested.
	Don't set LTLIBSTDTHREAD.
	* modules/thrd (Link): Simplify accordingly.
	* modules/mtx (Link): Likewise.
	* modules/cnd (Link): Likewise.
	* modules/tss (Link): Likewise.
	* modules/threads (Link): Likewise.

diff --git a/m4/threads.m4 b/m4/threads.m4
index 3a921c0..87e97f3 100644
--- a/m4/threads.m4
+++ b/m4/threads.m4
@@ -1,4 +1,4 @@
-# threads.m4 serial 2
+# threads.m4 serial 3
 dnl Copyright (C) 2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -11,6 +11,12 @@ AC_DEFUN([gl_THREADS_H],
   AC_REQUIRE([gl_THREADLIB_BODY])
   AC_REQUIRE([gl_YIELD])
 
+  if test "$gl_use_threads" = pth; then
+    AC_MSG_ERROR([You cannot use --enable-threads=pth with the gnulib module 'threads-h'.])
+  fi
+  dnl Now, since $gl_use_threads is not 'pth', $LTLIBMULTITHREAD and
+  dnl $LIBMULTITHREAD have the same value. Only system libraries are needed.
+
   gl_CHECK_NEXT_HEADERS([threads.h])
   if test $ac_cv_header_threads_h = yes; then
     HAVE_THREADS_H=1
@@ -47,7 +53,6 @@ AC_DEFUN([gl_THREADS_H],
   case "$host_os" in
     mingw*)
       LIBSTDTHREAD=
-      LTLIBSTDTHREAD=
       ;;
     *)
       if test $ac_cv_header_threads_h = yes; then
@@ -58,26 +63,21 @@ AC_DEFUN([gl_THREADS_H],
         AC_CHECK_FUNCS([thrd_create])
         if test $ac_cv_func_thrd_create = yes; then
           LIBSTDTHREAD=
-          LTLIBSTDTHREAD=
         else
           AC_CHECK_LIB([stdthreads], [thrd_create], [
             LIBSTDTHREAD='-lstdthreads -lpthread'
-            LTLIBSTDTHREAD='-lstdthreads -lpthread'
           ], [
             dnl Guess that thrd_create is in libpthread.
             LIBSTDTHREAD="$LIBMULTITHREAD"
-            LTLIBSTDTHREAD="$LTLIBMULTITHREAD"
           ])
         fi
       else
         dnl Libraries needed by thrd.c, mtx.c, cnd.c, tss.c.
         LIBSTDTHREAD="$LIBMULTITHREAD $YIELD_LIB"
-        LTLIBSTDTHREAD="$LTLIBMULTITHREAD $YIELD_LIB"
       fi
       ;;
   esac
   AC_SUBST([LIBSTDTHREAD])
-  AC_SUBST([LTLIBSTDTHREAD])
 
   AH_VERBATIM([thread_local],
 [/* The _Thread_local keyword of C11.  */
diff --git a/modules/cnd b/modules/cnd
index 635a4224..6870479 100644
--- a/modules/cnd
+++ b/modules/cnd
@@ -22,7 +22,7 @@ Include:
 <threads.h>
 
 Link:
-$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+$(LIBSTDTHREAD)
 
 License:
 LGPLv2+
diff --git a/modules/mtx b/modules/mtx
index a1d2a8a..ade00b2 100644
--- a/modules/mtx
+++ b/modules/mtx
@@ -25,7 +25,7 @@ Include:
 <threads.h>
 
 Link:
-$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+$(LIBSTDTHREAD)
 
 License:
 LGPLv2+
diff --git a/modules/thrd b/modules/thrd
index 3fd6063..388e0ea 100644
--- a/modules/thrd
+++ b/modules/thrd
@@ -24,7 +24,7 @@ Include:
 <threads.h>
 
 Link:
-$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+$(LIBSTDTHREAD)
 
 License:
 LGPLv2+
diff --git a/modules/threads b/modules/threads
index 2693bd5..18391c1 100644
--- a/modules/threads
+++ b/modules/threads
@@ -18,7 +18,7 @@ Include:
 <threads.h>
 
 Link:
-$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+$(LIBSTDTHREAD)
 
 License:
 LGPLv2+
diff --git a/modules/tss b/modules/tss
index 4a28200..d751724 100644
--- a/modules/tss
+++ b/modules/tss
@@ -21,7 +21,7 @@ Include:
 <threads.h>
 
 Link:
-$(LTLIBSTDTHREAD) when linking with libtool, $(LIBSTDTHREAD) otherwise
+$(LIBSTDTHREAD)
 
 License:
 LGPLv2+



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
  2019-06-20 16:34   ` Paul Eggert
  2019-06-21  9:22   ` Bruno Haible
@ 2019-06-21  9:45   ` Bruno Haible
  2 siblings, 0 replies; 11+ messages in thread
From: Bruno Haible @ 2019-06-21  9:45 UTC (permalink / raw)
  To: bug-gnulib

This patch makes the 'thread_local' storage class actually usable on
many platforms.

I found that 'thread_local' works on the following platforms:
  * Linux with glibc
    on i386/x86_64/x32 (even on "old" systems such as CentOS 5), mips/mips64,
       sparc/sparc64, alpha, arm, arm64, powerpc/powerpc64, ia64, s390/s390x,
       riscv64, m68k, hppa
  * Linux with musl libc
    on i386/x86_64
  * GNU/kFreeBSD
    on i386/x86_64
  * Hurd
    on i386
  * Mac OS X 10.13
    on x86_64
  * FreeBSD 11 and MidnightBSD 0.8
    on i386/x86_64, arm64
  * NetBSD 7
    on i386/x86_64
  * AIX 7 (with 'xlc -qthreaded -qtls')
  * AIX 7.2 (with gcc)
  * HP-UX 11.31 (with gcc)
  * Solaris 10
    on i386/x86_64, sparc/sparc64
  * Solaris 11.4
    on i386/x86_64
  * illumos (OpenIndiana)
    on i386/x86_64
  * Cygwin
    on i386/x86_64
  * mingw
    on i386/x86_64
  * Haiku
    on i386

It does not work (__thread not understood by the compiler and linker)
on the following platforms:
  * Mac OS X 10.5
    on i386/x86_64, powerpc
  * OpenBSD 6.5
  * HP-UX 11.31 (with cc)
  * IRIX 6.5 (with gcc)

It does not work (__thread not understood by the compiler and linker
but the test program crashes) on the following platforms:
  * AIX 7.1 (with gcc)
  * Android 4.3


2019-06-21  Bruno Haible  <bruno@clisp.org>

	threads-h: Define 'thread_local' if and only it actually works.
	* m4/threads.m4 (gl_THREAD_LOCAL_DEFINITION): New macro.
	(gl_THREADS_H): Define _Thread_local to __thread also for ARM C, IBM C,
	Oracle Solaris Studio C. Compile a simple program, to see whether
	_Thread_local basically works. Set HAVE_THREAD_LOCAL and LIBTHREADLOCAL.
	(gl_THREADS_H_DEFAULTS): Initialize HAVE_THREAD_LOCAL.
	* lib/threads.in.h (thread_local): Undefine if it does not work.
	* modules/threads-h (Makefile.am): Substitute HAVE_THREAD_LOCAL.
	(Link): Mention LIBTHREADLOCAL.
	* tests/test-threads.c: Don't check that thread_local is defined.
	* tests/test-thread_local.c: New file.
	* modules/threads-h-tests (Files): Add it and macros.h.
	(Depends-on): Add thrd and stdint.
	(configure.ac): Test whether 'alarm' is declared.
	(Makefile.am): Arrange to build and link test-thread_local.
	* doc/posix-headers/threads.texi: Mention the platforms that don't
	support 'thread_local'.

diff --git a/doc/posix-headers/threads.texi b/doc/posix-headers/threads.texi
index 71ae43a..d3bc66f 100644
--- a/doc/posix-headers/threads.texi
+++ b/doc/posix-headers/threads.texi
@@ -20,4 +20,20 @@ AIX 7.2.
 
 Portability problems not fixed by Gnulib:
 @itemize
+@item
+There is no way to define a working @code{thread_local} macro on some platforms:
+@itemize
+@item
+Mac OS X 10.5,
+@item
+OpenBSD 6.5,
+@item
+AIX 7.1 with gcc (but it works with @samp{xlc -qthreaded -qtls}),
+@item
+HP-UX 11.31 with cc (but it works with gcc),
+@item
+IRIX 6.5,
+@item
+Android 4.3.
+@end itemize
 @end itemize
diff --git a/lib/threads.in.h b/lib/threads.in.h
index a18d64b..b7fed72 100644
--- a/lib/threads.in.h
+++ b/lib/threads.in.h
@@ -67,6 +67,10 @@
 #if !@HAVE_THREADS_H@ || !defined thread_local
 # define thread_local _Thread_local
 #endif
+/* Define the macro thread_local if and only if it actually works.  */
+#if !@HAVE_THREAD_LOCAL@
+# undef thread_local
+#endif
 
 
 /* =========== ISO C 11 7.26.5 Thread functions =========== */
diff --git a/m4/threads.m4 b/m4/threads.m4
index 87e97f3..1aebb0a 100644
--- a/m4/threads.m4
+++ b/m4/threads.m4
@@ -1,9 +1,14 @@
-# threads.m4 serial 3
+# threads.m4 serial 4
 dnl Copyright (C) 2019 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.
 
+dnl Tests whether the <threads.h> facility is available.
+dnl Sets the variable LIBSTDTHREAD to the linker options for use in a Makefile
+dnl for a program that uses the <threads.h> functions.
+dnl Sets the variable LIBTHREADLOCAL to the linker options for use in a Makefile
+dnl for a program that uses the 'thread_local' macro.
 AC_DEFUN([gl_THREADS_H],
 [
   AC_REQUIRE([gl_THREADS_H_DEFAULTS])
@@ -79,16 +84,54 @@ AC_DEFUN([gl_THREADS_H],
   esac
   AC_SUBST([LIBSTDTHREAD])
 
-  AH_VERBATIM([thread_local],
-[/* The _Thread_local keyword of C11.  */
-#ifndef _Thread_local
-# if defined __GNUC__
-#  define _Thread_local __thread
-# elif defined _MSC_VER
-#  define _Thread_local __declspec (thread)
-# endif
-#endif
-])
+  dnl Define _Thread_local.
+  dnl GCC, for example, supports '__thread' since version 3.3, but it supports
+  dnl '_Thread_local' only starting with version 4.9.
+  AH_VERBATIM([thread_local], gl_THREAD_LOCAL_DEFINITION)
+
+  dnl Test whether _Thread_local is supported in the compiler and linker.
+  AC_CACHE_CHECK([whether _Thread_local works],
+    [gl_cv_thread_local_works],
+    [dnl On AIX 7.1 with GCC 4.8.1, this test program compiles fine, but the
+     dnl 'test-thread-local' test misbehaves.
+     dnl On Android 4.3, this test program compiles fine, but the
+     dnl 'test-thread-local' test crashes.
+     if case "$host_os" in
+          aix*) test -n "$GCC" ;;
+          linux*-android*) true ;;
+          *) false ;;
+        esac
+     then
+       gl_cv_thread_local_works="guessing no"
+     else
+       AC_COMPILE_IFELSE(
+         [AC_LANG_PROGRAM([gl_THREAD_LOCAL_DEFINITION[
+            int _Thread_local x;
+          ]], [[
+            x = 42;
+          ]])],
+         [gl_cv_thread_local_works=yes],
+         [gl_cv_thread_local_works=no])
+     fi
+    ])
+  case "$gl_cv_thread_local_works" in
+    *yes) ;;
+    *) HAVE_THREAD_LOCAL=0 ;;
+  esac
+
+  dnl Determine the link dependencies of '_Thread_local'.
+  LIBTHREADLOCAL=
+  dnl On AIX 7.2 with "xlc -qthreaded -qtls", programs that use _Thread_local
+  dnl as defined above produce link errors regarding the symbols
+  dnl '.__tls_get_mod' and '__tls_get_addr'. Similarly, on AIX 7.2 with gcc,
+  dnl 32-bit programs that use _Thread_local produce link errors regarding the
+  dnl symbol '__get_tpointer'. The fix is to link with -lpthread.
+  case "$host_os" in
+    aix*)
+      LIBTHREADLOCAL=-lpthread
+      ;;
+  esac
+  AC_SUBST([LIBTHREADLOCAL])
 
   dnl Check for declarations of anything we want to poison if the
   dnl corresponding gnulib module is not in use, and which is not
@@ -102,6 +145,24 @@ AC_DEFUN([gl_THREADS_H],
     tss_create tss_delete tss_get tss_set])
 ])
 
+dnl Expands to C preprocessor statements that define _Thread_local.
+AC_DEFUN([gl_THREAD_LOCAL_DEFINITION],
+[[/* The _Thread_local keyword of C11.  */
+/* GNU C: <https://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html> */
+/* ARM C: <https://developer.arm.com/docs/dui0472/latest/compiler-specific-features/__declspecthread> */
+/* IBM C: supported only with compiler option -qtls, see
+   <https://www.ibm.com/support/knowledgecenter/SSGH2K_12.1.0/com.ibm.xlc121.aix.doc/compiler_ref/opt_tls.html> */
+/* Oracle Solaris Studio C: <https://docs.oracle.com/cd/E18659_01/html/821-1384/bjabr.html> */
+/* MSVC: <https://docs.microsoft.com/en-us/cpp/parallel/thread-local-storage-tls> */
+#ifndef _Thread_local
+# if defined __GNUC__ || defined __CC_ARM || defined __xlC__ || defined __SUNPRO_C
+#  define _Thread_local __thread
+# elif defined _MSC_VER
+#  define _Thread_local __declspec (thread)
+# endif
+#endif
+]])
+
 AC_DEFUN([gl_THREADS_MODULE_INDICATOR],
 [
   dnl Use AC_REQUIRE here, so that the default settings are expanded once only.
@@ -118,6 +179,7 @@ AC_DEFUN([gl_THREADS_H_DEFAULTS],
   GNULIB_THRD=0;          AC_SUBST([GNULIB_THRD])
   GNULIB_TSS=0;           AC_SUBST([GNULIB_TSS])
   dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_THREAD_LOCAL=1;    AC_SUBST([HAVE_THREAD_LOCAL])
   BROKEN_THRD_START_T=0;  AC_SUBST([BROKEN_THRD_START_T])
   REPLACE_THRD_CREATE=0;  AC_SUBST([REPLACE_THRD_CREATE])
   REPLACE_THRD_CURRENT=0; AC_SUBST([REPLACE_THRD_CURRENT])
diff --git a/modules/threads-h b/modules/threads-h
index 238955c..c5af53e 100644
--- a/modules/threads-h
+++ b/modules/threads-h
@@ -50,6 +50,7 @@ threads.h: threads.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(_NORETURN_H
 	      -e 's/@''GNULIB_MTX''@/$(GNULIB_MTX)/g' \
 	      -e 's/@''GNULIB_THRD''@/$(GNULIB_THRD)/g' \
 	      -e 's/@''GNULIB_TSS''@/$(GNULIB_TSS)/g' \
+	      -e 's|@''HAVE_THREAD_LOCAL''@|$(HAVE_THREAD_LOCAL)|g' \
 	      -e 's|@''BROKEN_THRD_START_T''@|$(BROKEN_THRD_START_T)|g' \
 	      -e 's|@''REPLACE_THRD_CREATE''@|$(REPLACE_THRD_CREATE)|g' \
 	      -e 's|@''REPLACE_THRD_CURRENT''@|$(REPLACE_THRD_CURRENT)|g' \
@@ -65,6 +66,7 @@ Include:
 <threads.h>
 
 Link:
+$(LIBTHREADLOCAL) if you use the thread_local macro
 
 License:
 LGPLv2+
diff --git a/modules/threads-h-tests b/modules/threads-h-tests
index e4f399d..abbd43a 100644
--- a/modules/threads-h-tests
+++ b/modules/threads-h-tests
@@ -1,12 +1,18 @@
 Files:
 tests/test-threads.c
+tests/test-thread_local.c
+tests/macros.h
 
 Depends-on:
 threads-h-c++-tests
+thrd
+stdint
 
 configure.ac:
+AC_CHECK_DECLS_ONCE([alarm])
 
 Makefile.am:
-TESTS += test-threads
-check_PROGRAMS += test-threads
+TESTS += test-threads test-thread_local
+check_PROGRAMS += test-threads test-thread_local
 test_threads_LDADD = $(LDADD) @LIBSTDTHREAD@
+test_thread_local_LDADD = $(LDADD) @LIBSTDTHREAD@ @LIBTHREADLOCAL@
diff --git a/tests/test-thread_local.c b/tests/test-thread_local.c
new file mode 100644
index 0000000..fddf455
--- /dev/null
+++ b/tests/test-thread_local.c
@@ -0,0 +1,196 @@
+/* Test of thread-local storage in multithreaded situations.
+   Copyright (C) 2005, 2008-2019 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 <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */
+
+#include <config.h>
+
+#include <threads.h>
+
+#ifdef thread_local
+
+/* 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
+
+/* Number of simultaneous threads.  */
+#define THREAD_COUNT 16
+
+/* Number of operations performed in each thread.  */
+#define REPEAT_COUNT 50000
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+
+#include "macros.h"
+
+#if ENABLE_DEBUGGING
+# define dbgprintf printf
+#else
+# define dbgprintf if (0) printf
+#endif
+
+#if EXPLICIT_YIELD
+# define yield() thrd_yield ()
+#else
+# define yield()
+#endif
+
+/* Returns a reference to the current thread as a pointer, for debugging.  */
+#if defined __MVS__
+  /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
+     The first three bytes of this field appear to uniquely identify a
+     pthread_t, though not necessarily representing a pointer.  */
+# define thrd_current_pointer() (*((void **) thrd_current ().__))
+#elif defined __sun
+  /* On Solaris, thrd_t is merely an 'unsigned int'.  */
+# define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
+#else
+# define thrd_current_pointer() ((void *) thrd_current ())
+#endif
+
+static void
+perhaps_yield (void)
+{
+  /* Call yield () only with a certain probability, otherwise the
+     sequence of thread activations may be too predictable.  */
+  if ((((unsigned int) rand () >> 3) % 4) == 0)
+    yield ();
+}
+
+
+/* ----------------------- Test thread-local storage ----------------------- */
+
+#define KEYS_COUNT 4
+static unsigned int thread_local value0;
+static unsigned int thread_local value1;
+static unsigned int thread_local value2;
+static unsigned int thread_local value3;
+
+static int
+worker_thread (void *arg)
+{
+  unsigned int id = (unsigned int) (uintptr_t) arg;
+  int i, j, repeat;
+  unsigned int *values[KEYS_COUNT] = { &value0, &value1, &value2, &value3 };
+
+  dbgprintf ("Worker %p started\n", thrd_current_pointer ());
+
+  /* Initialize the per-thread storage.  */
+  dbgprintf ("Worker %p before first assignment\n", thrd_current_pointer ());
+  for (i = 0; i < KEYS_COUNT; i++)
+    {
+      *values[i] = (((unsigned int) rand () >> 3) % 1000000) * THREAD_COUNT + id;
+      /* Hopefully no arithmetic overflow.  */
+      if ((*values[i] % THREAD_COUNT) != id)
+        abort ();
+    }
+  dbgprintf ("Worker %p after  first assignment\n", thrd_current_pointer ());
+  perhaps_yield ();
+
+  /* Shuffle around the pointers.  */
+  for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
+    {
+      dbgprintf ("Worker %p doing value swapping\n", thrd_current_pointer ());
+      i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
+      j = ((unsigned int) rand () >> 3) % KEYS_COUNT;
+      if (i != j)
+        {
+          unsigned int vi = *values[i];
+          unsigned int vj = *values[j];
+
+          *values[i] = vj;
+          *values[j] = vi;
+        }
+      perhaps_yield ();
+    }
+
+  /* Verify that all the values are from this thread.  */
+  dbgprintf ("Worker %p before final verify\n", thrd_current_pointer ());
+  for (i = 0; i < KEYS_COUNT; i++)
+    if ((*values[i] % THREAD_COUNT) != id)
+      abort ();
+  dbgprintf ("Worker %p after  final verify\n", thrd_current_pointer ());
+  perhaps_yield ();
+
+  dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
+  return 0;
+}
+
+static void
+test_thread_local (void)
+{
+  int pass, i;
+
+  for (pass = 0; pass < 2; pass++)
+    {
+      thrd_t threads[THREAD_COUNT];
+
+      /* Spawn the threads.  */
+      for (i = 0; i < THREAD_COUNT; i++)
+        ASSERT (thrd_create (&threads[i], worker_thread, (void *) (uintptr_t) i)
+                == thrd_success);
+
+      /* Wait for the threads to terminate.  */
+      for (i = 0; i < THREAD_COUNT; i++)
+        ASSERT (thrd_join (threads[i], NULL) == thrd_success);
+    }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+int
+main ()
+{
+#if HAVE_DECL_ALARM
+  /* Declare failure if test takes too long, by using default abort
+     caused by SIGALRM.  */
+  int alarm_value = 600;
+  signal (SIGALRM, SIG_DFL);
+  alarm (alarm_value);
+#endif
+
+  printf ("Starting test_thread_local ..."); fflush (stdout);
+  test_thread_local ();
+  printf (" OK\n"); fflush (stdout);
+
+  return 0;
+}
+
+#else
+
+/* No thread-local storage support available in the compiler and linker.  */
+
+#include <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: thread_local not supported\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-threads.c b/tests/test-threads.c
index 39a0f3c..716fceb 100644
--- a/tests/test-threads.c
+++ b/tests/test-threads.c
@@ -20,10 +20,8 @@
 
 #include <threads.h>
 
-/* Check that thread_local is defined.  */
-#ifndef thread_local
-"oops"
-#endif
+/* Don't check that thread_local is defined.
+   We cannot define it properly on some platforms.  */
 
 /* Check that ONCE_FLAG_INIT is defined.  */
 #ifndef ONCE_FLAG_INIT



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-06-20 17:50     ` Bruno Haible
@ 2019-07-01  2:46       ` Bruno Haible
  2019-07-04  6:49         ` Paul Eggert
  0 siblings, 1 reply; 11+ messages in thread
From: Bruno Haible @ 2019-07-01  2:46 UTC (permalink / raw)
  To: Paul Eggert; +Cc: bug-gnulib

Hi Paul,

I wrote:
> You can go a long way with <pthread.h>. ...
> The use of <threads.h> is very similar to the use of <pthread.h>.

In fact, it should be possible to reuse the bulk of the <threads.h>
and gnulib 'threads'/'lock'/... modules to provide an implementation
of the essential <pthread.h> API on native Windows (both mingw and MSVC).

Whereas currently, these functions are stubs in gnulib's <pthread.h>.

With this, we would have the essential pthread_* functions implemented
on all reasonable portability targets, except Minix. (*) Programs
like coreutils 'sort' would be ported to native Windows with no effort.

I can work on that, if you have no objections.

Bruno

(*) Minix has "mthreads". Some volunteer may work on wrapping these in the
    pthread API.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] ISO C 11 threads implementation
  2019-07-01  2:46       ` Bruno Haible
@ 2019-07-04  6:49         ` Paul Eggert
  0 siblings, 0 replies; 11+ messages in thread
From: Paul Eggert @ 2019-07-04  6:49 UTC (permalink / raw)
  To: Bruno Haible; +Cc: bug-gnulib

Bruno Haible wrote:
> I can work on that, if you have no objections.

Sounds good, and thanks for looking into that.


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2019-07-04  6:49 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-20 10:40 ISO C 11 threads Bruno Haible
2019-06-20 10:42 ` [PATCH] ISO C 11 threads preparations Bruno Haible
2019-06-20 10:45 ` [PATCH] ISO C 11 threads implementation Bruno Haible
2019-06-20 16:34   ` Paul Eggert
2019-06-20 17:50     ` Bruno Haible
2019-07-01  2:46       ` Bruno Haible
2019-07-04  6:49         ` Paul Eggert
2019-06-21  9:22   ` Bruno Haible
2019-06-21  9:45   ` Bruno Haible
2019-06-20 17:19 ` ISO C 11 threads Bruno Haible
2019-06-21  1:12 ` Bruno Haible

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).