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
next prev parent 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).