bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
From: Bruno Haible <bruno@clisp.org>
To: bug-gnulib@gnu.org
Subject: [PATCH] ISO C 11 threads preparations
Date: Thu, 20 Jun 2019 12:42:50 +0200	[thread overview]
Message-ID: <23309537.22YT0vlu5s@omega> (raw)
In-Reply-To: <2006820.U7yMURXysu@omega>

[-- 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


  reply	other threads:[~2019-06-20 11:05 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-20 10:40 ISO C 11 threads Bruno Haible
2019-06-20 10:42 ` Bruno Haible [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://lists.gnu.org/mailman/listinfo/bug-gnulib

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=23309537.22YT0vlu5s@omega \
    --to=bruno@clisp.org \
    --cc=bug-gnulib@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).