>From 721d00b4505054875aa5530c230ca9ede5452d96 Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Wed, 27 Nov 2019 06:03:21 +0100
Subject: [PATCH] New options --enable-threads=isoc and
--enable-threads=isoc+posix.
* m4/threadlib.m4 (gl_THREADLIB_EARLY_BODY): Accept the options
--enable-threads=isoc and --enable-threads=isoc+posix.
(gl_THREADLIB_BODY): Test whether the ISO C threads API is available.
When both the ISO C and the POSIX threads API are available, choose
USE_ISOC_AND_POSIX_THREADS instead of USE_POSIX_THREADS if
--enable-threads=isoc+posix was specified. When only the ISO C threads
API is available and --enable-threads=iso was specified, choose
USE_ISOC_THREADS.
* lib/glthread/lock.h: Add new code for USE_ISOC_THREADS ||
USE_ISOC_AND_POSIX_THREADS.
* lib/glthread/lock.c: Likewise.
* lib/glthread/cond.h: Likewise.
* lib/glthread/cond.c: Likewise.
* lib/glthread/tls.h: Likewise.
* lib/glthread/tls.c: Likewise.
* lib/glthread/yield.h: Likewise.
* lib/glthread/thread.h: Add new code for USE_ISOC_THREADS. Treat
USE_ISOC_AND_POSIX_THREADS like USE_POSIX_THREADS.
* lib/glthread/thread.c: Likewise.
* lib/glthread/threadlib.c: Likewise.
* tests/test-lock.c: Save and restore the values of USE_ISOC_THREADS and
USE_ISOC_AND_POSIX_THREADS.
* tests/test-cond.c: Consider USE_ISOC_THREADS and
USE_ISOC_AND_POSIX_THREADS.
* tests/test-tls.c: Likewise.
* tests/test-thread_create.c (main): Likewise.
* tests/test-pthread-cond.c: Likewise.
* tests/test-pthread-mutex.c: Likewise.
* tests/test-pthread-once2.c: Likewise.
* tests/test-pthread-rwlock.c: Likewise.
* tests/test-pthread-tss.c: Likewise.
* tests/test-pthread_sigmask2.c: Treat USE_ISOC_AND_POSIX_THREADS like
USE_POSIX_THREADS.
---
ChangeLog | 37 +++++++
lib/glthread/cond.c | 82 ++++++++++++++++
lib/glthread/cond.h | 49 +++++++++-
lib/glthread/lock.c | 223 ++++++++++++++++++++++++++++++++++++++++++
lib/glthread/lock.h | 111 ++++++++++++++++++++-
lib/glthread/thread.c | 158 +++++++++++++++++++++++++++++-
lib/glthread/thread.h | 36 ++++++-
lib/glthread/threadlib.c | 2 +-
lib/glthread/tls.c | 6 ++
lib/glthread/tls.h | 24 ++++-
lib/glthread/yield.h | 23 ++++-
m4/threadlib.m4 | 73 +++++++++++---
tests/test-cond.c | 2 +-
tests/test-lock.c | 16 ++-
tests/test-pthread-cond.c | 2 +-
tests/test-pthread-mutex.c | 2 +-
tests/test-pthread-once2.c | 2 +-
tests/test-pthread-rwlock.c | 2 +-
tests/test-pthread-tss.c | 2 +-
tests/test-pthread_sigmask2.c | 2 +-
tests/test-thread_create.c | 2 +-
tests/test-tls.c | 5 +-
22 files changed, 827 insertions(+), 34 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index d612dce..bd2330e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2019-11-27 Bruno Haible
+
+ New options --enable-threads=isoc and --enable-threads=isoc+posix.
+ * m4/threadlib.m4 (gl_THREADLIB_EARLY_BODY): Accept the options
+ --enable-threads=isoc and --enable-threads=isoc+posix.
+ (gl_THREADLIB_BODY): Test whether the ISO C threads API is available.
+ When both the ISO C and the POSIX threads API are available, choose
+ USE_ISOC_AND_POSIX_THREADS instead of USE_POSIX_THREADS if
+ --enable-threads=isoc+posix was specified. When only the ISO C threads
+ API is available and --enable-threads=iso was specified, choose
+ USE_ISOC_THREADS.
+ * lib/glthread/lock.h: Add new code for USE_ISOC_THREADS ||
+ USE_ISOC_AND_POSIX_THREADS.
+ * lib/glthread/lock.c: Likewise.
+ * lib/glthread/cond.h: Likewise.
+ * lib/glthread/cond.c: Likewise.
+ * lib/glthread/tls.h: Likewise.
+ * lib/glthread/tls.c: Likewise.
+ * lib/glthread/yield.h: Likewise.
+ * lib/glthread/thread.h: Add new code for USE_ISOC_THREADS. Treat
+ USE_ISOC_AND_POSIX_THREADS like USE_POSIX_THREADS.
+ * lib/glthread/thread.c: Likewise.
+ * lib/glthread/threadlib.c: Likewise.
+ * tests/test-lock.c: Save and restore the values of USE_ISOC_THREADS and
+ USE_ISOC_AND_POSIX_THREADS.
+ * tests/test-cond.c: Consider USE_ISOC_THREADS and
+ USE_ISOC_AND_POSIX_THREADS.
+ * tests/test-tls.c: Likewise.
+ * tests/test-thread_create.c (main): Likewise.
+ * tests/test-pthread-cond.c: Likewise.
+ * tests/test-pthread-mutex.c: Likewise.
+ * tests/test-pthread-once2.c: Likewise.
+ * tests/test-pthread-rwlock.c: Likewise.
+ * tests/test-pthread-tss.c: Likewise.
+ * tests/test-pthread_sigmask2.c: Treat USE_ISOC_AND_POSIX_THREADS like
+ USE_POSIX_THREADS.
+
2019-11-24 Bruno Haible
mbrtowc: Modernize autoconf test.
diff --git a/lib/glthread/cond.c b/lib/glthread/cond.c
index f6a06cc..df166bf 100644
--- a/lib/glthread/cond.c
+++ b/lib/glthread/cond.c
@@ -24,6 +24,88 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+int
+glthread_cond_init (gl_cond_t *condition)
+{
+ if (cnd_init (&condition->condition) != thrd_success)
+ return ENOMEM;
+ condition->init_needed = 0;
+ return 0;
+}
+
+int
+glthread_cond_wait (gl_cond_t *condition, gl_lock_t *lock)
+{
+ if (condition->init_needed)
+ call_once (&condition->init_once, condition->init_func);
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (cnd_wait (&condition->condition, &lock->mutex) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_cond_timedwait (gl_cond_t *condition, gl_lock_t *lock,
+ const struct timespec *abstime)
+{
+ if (condition->init_needed)
+ call_once (&condition->init_once, condition->init_func);
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ switch (cnd_timedwait (&condition->condition, &lock->mutex, abstime))
+ {
+ case thrd_success:
+ break;
+ case thrd_timedout:
+ return ETIMEDOUT;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+int
+glthread_cond_signal (gl_cond_t *condition)
+{
+ if (condition->init_needed)
+ call_once (&condition->init_once, condition->init_func);
+ if (cnd_signal (&condition->condition) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_cond_broadcast (gl_cond_t *condition)
+{
+ if (condition->init_needed)
+ call_once (&condition->init_once, condition->init_func);
+ if (cnd_broadcast (&condition->condition) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_cond_destroy (gl_cond_t *condition)
+{
+ if (condition->init_needed)
+ call_once (&condition->init_once, condition->init_func);
+ cnd_destroy (&condition->condition);
+ return 0;
+}
+
+#endif
+
+/* ========================================================================= */
+
+#if USE_POSIX_THREADS
+
+#endif
+
+/* ========================================================================= */
+
#if USE_WINDOWS_THREADS
#endif
diff --git a/lib/glthread/cond.h b/lib/glthread/cond.h
index 4c6f4d9..cd1ec98 100644
--- a/lib/glthread/cond.h
+++ b/lib/glthread/cond.h
@@ -76,6 +76,53 @@ _GL_INLINE_HEADER_BEGIN
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library. */
+
+# include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* -------------------------- gl_cond_t datatype -------------------------- */
+
+typedef struct
+ {
+ int volatile init_needed;
+ once_flag init_once;
+ void (*init_func) (void);
+ cnd_t condition;
+ }
+ gl_cond_t;
+# define gl_cond_define(STORAGECLASS, NAME) \
+ STORAGECLASS gl_cond_t NAME;
+# define gl_cond_define_initialized(STORAGECLASS, NAME) \
+ static void _atomic_init_##NAME (void); \
+ STORAGECLASS gl_cond_t NAME = \
+ { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
+ static void _atomic_init_##NAME (void) \
+ { \
+ if (glthread_cond_init (&(NAME))) \
+ abort (); \
+ }
+extern int glthread_cond_init (gl_cond_t *condition);
+extern int glthread_cond_wait (gl_cond_t *condition, gl_lock_t *lock);
+extern int glthread_cond_timedwait (gl_cond_t *condition, gl_lock_t *lock,
+ const struct timespec *abstime);
+extern int glthread_cond_signal (gl_cond_t *condition);
+extern int glthread_cond_broadcast (gl_cond_t *condition);
+extern int glthread_cond_destroy (gl_cond_t *condition);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
/* Use the POSIX threads library. */
@@ -214,7 +261,7 @@ typedef glwthread_cond_t gl_cond_t;
/* ========================================================================= */
-#if !(USE_POSIX_THREADS || USE_WINDOWS_THREADS)
+#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
/* Provide dummy implementation if threads are not supported. */
diff --git a/lib/glthread/lock.c b/lib/glthread/lock.c
index 852d84d..2537839 100644
--- a/lib/glthread/lock.c
+++ b/lib/glthread/lock.c
@@ -23,6 +23,229 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* -------------------------- gl_lock_t datatype -------------------------- */
+
+int
+glthread_lock_init (gl_lock_t *lock)
+{
+ if (mtx_init (&lock->mutex, mtx_plain) != thrd_success)
+ return ENOMEM;
+ lock->init_needed = 0;
+ return 0;
+}
+
+int
+glthread_lock_lock (gl_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_lock (&lock->mutex) != thrd_success)
+ return EAGAIN;
+ return 0;
+}
+
+int
+glthread_lock_unlock (gl_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_unlock (&lock->mutex) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_lock_destroy (gl_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ mtx_destroy (&lock->mutex);
+ return 0;
+}
+
+/* ------------------------- gl_rwlock_t datatype ------------------------- */
+
+int
+glthread_rwlock_init (gl_rwlock_t *lock)
+{
+ if (mtx_init (&lock->lock, mtx_plain) != thrd_success
+ || cnd_init (&lock->waiting_readers) != thrd_success
+ || cnd_init (&lock->waiting_writers) != thrd_success)
+ return ENOMEM;
+ lock->waiting_writers_count = 0;
+ lock->runcount = 0;
+ lock->init_needed = 0;
+ return 0;
+}
+
+int
+glthread_rwlock_rdlock (gl_rwlock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_lock (&lock->lock) != thrd_success)
+ return EAGAIN;
+ /* Test whether only readers are currently running, and whether the runcount
+ field will not overflow, and whether no writer is waiting. The latter
+ condition is because POSIX recommends that "write locks shall take
+ precedence over read locks", to avoid "writer starvation". */
+ while (!(lock->runcount + 1 > 0 && lock->waiting_writers_count == 0))
+ {
+ /* This thread has to wait for a while. Enqueue it among the
+ waiting_readers. */
+ if (cnd_wait (&lock->waiting_readers, &lock->lock) != thrd_success)
+ {
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ }
+ lock->runcount++;
+ if (mtx_unlock (&lock->lock) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_rwlock_wrlock (gl_rwlock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_lock (&lock->lock) != thrd_success)
+ return EAGAIN;
+ /* Test whether no readers or writers are currently running. */
+ while (!(lock->runcount == 0))
+ {
+ /* This thread has to wait for a while. Enqueue it among the
+ waiting_writers. */
+ lock->waiting_writers_count++;
+ if (cnd_wait (&lock->waiting_writers, &lock->lock) != thrd_success)
+ {
+ lock->waiting_writers_count--;
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ lock->waiting_writers_count--;
+ }
+ lock->runcount--; /* runcount becomes -1 */
+ if (mtx_unlock (&lock->lock) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_rwlock_unlock (gl_rwlock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_lock (&lock->lock) != thrd_success)
+ return EAGAIN;
+ if (lock->runcount < 0)
+ {
+ /* Drop a writer lock. */
+ if (!(lock->runcount == -1))
+ {
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ lock->runcount = 0;
+ }
+ else
+ {
+ /* Drop a reader lock. */
+ if (!(lock->runcount > 0))
+ {
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ lock->runcount--;
+ }
+ if (lock->runcount == 0)
+ {
+ /* POSIX recommends that "write locks shall take precedence over read
+ locks", to avoid "writer starvation". */
+ if (lock->waiting_writers_count > 0)
+ {
+ /* Wake up one of the waiting writers. */
+ if (cnd_signal (&lock->waiting_writers) != thrd_success)
+ {
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ }
+ else
+ {
+ /* Wake up all waiting readers. */
+ if (cnd_broadcast (&lock->waiting_readers) != thrd_success)
+ {
+ mtx_unlock (&lock->lock);
+ return EINVAL;
+ }
+ }
+ }
+ if (mtx_unlock (&lock->lock) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_rwlock_destroy (gl_rwlock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ mtx_destroy (&lock->lock);
+ cnd_destroy (&lock->waiting_readers);
+ cnd_destroy (&lock->waiting_writers);
+ return 0;
+}
+
+/* --------------------- gl_recursive_lock_t datatype --------------------- */
+
+int
+glthread_recursive_lock_init (gl_recursive_lock_t *lock)
+{
+ if (mtx_init (&lock->mutex, mtx_plain | mtx_recursive) != thrd_success)
+ return ENOMEM;
+ lock->init_needed = 0;
+ return 0;
+}
+
+int
+glthread_recursive_lock_lock (gl_recursive_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_lock (&lock->mutex) != thrd_success)
+ return EAGAIN;
+ return 0;
+}
+
+int
+glthread_recursive_lock_unlock (gl_recursive_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ if (mtx_unlock (&lock->mutex) != thrd_success)
+ return EINVAL;
+ return 0;
+}
+
+int
+glthread_recursive_lock_destroy (gl_recursive_lock_t *lock)
+{
+ if (lock->init_needed)
+ call_once (&lock->init_once, lock->init_func);
+ mtx_destroy (&lock->mutex);
+ return 0;
+}
+
+/* -------------------------- gl_once_t datatype -------------------------- */
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
/* -------------------------- gl_lock_t datatype -------------------------- */
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 78dd67f..ffbec15 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -92,6 +92,115 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library. */
+
+# include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* -------------------------- gl_lock_t datatype -------------------------- */
+
+typedef struct
+ {
+ int volatile init_needed;
+ once_flag init_once;
+ void (*init_func) (void);
+ mtx_t mutex;
+ }
+ gl_lock_t;
+# define gl_lock_define(STORAGECLASS, NAME) \
+ STORAGECLASS gl_lock_t NAME;
+# define gl_lock_define_initialized(STORAGECLASS, NAME) \
+ static void _atomic_init_##NAME (void); \
+ STORAGECLASS gl_lock_t NAME = \
+ { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
+ static void _atomic_init_##NAME (void) \
+ { \
+ if (glthread_lock_init (&(NAME))) \
+ abort (); \
+ }
+extern int glthread_lock_init (gl_lock_t *lock);
+extern int glthread_lock_lock (gl_lock_t *lock);
+extern int glthread_lock_unlock (gl_lock_t *lock);
+extern int glthread_lock_destroy (gl_lock_t *lock);
+
+/* ------------------------- gl_rwlock_t datatype ------------------------- */
+
+typedef struct
+ {
+ int volatile init_needed;
+ once_flag init_once;
+ void (*init_func) (void);
+ mtx_t lock; /* protects the remaining fields */
+ cnd_t waiting_readers; /* waiting readers */
+ cnd_t waiting_writers; /* waiting writers */
+ unsigned int waiting_writers_count; /* number of waiting writers */
+ int runcount; /* number of readers running, or -1 when a writer runs */
+ }
+ gl_rwlock_t;
+# define gl_rwlock_define(STORAGECLASS, NAME) \
+ STORAGECLASS gl_rwlock_t NAME;
+# define gl_rwlock_define_initialized(STORAGECLASS, NAME) \
+ static void _atomic_init_##NAME (void); \
+ STORAGECLASS gl_rwlock_t NAME = \
+ { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
+ static void _atomic_init_##NAME (void) \
+ { \
+ if (glthread_rwlock_init (&(NAME))) \
+ abort (); \
+ }
+extern int glthread_rwlock_init (gl_rwlock_t *lock);
+extern int glthread_rwlock_rdlock (gl_rwlock_t *lock);
+extern int glthread_rwlock_wrlock (gl_rwlock_t *lock);
+extern int glthread_rwlock_unlock (gl_rwlock_t *lock);
+extern int glthread_rwlock_destroy (gl_rwlock_t *lock);
+
+/* --------------------- gl_recursive_lock_t datatype --------------------- */
+
+typedef struct
+ {
+ int volatile init_needed;
+ once_flag init_once;
+ void (*init_func) (void);
+ mtx_t mutex;
+ }
+ 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) \
+ static void _atomic_init_##NAME (void); \
+ STORAGECLASS gl_recursive_lock_t NAME = \
+ { 1, ONCE_FLAG_INIT, _atomic_init_##NAME }; \
+ static void _atomic_init_##NAME (void) \
+ { \
+ if (glthread_recursive_lock_init (&(NAME))) \
+ abort (); \
+ }
+extern int glthread_recursive_lock_init (gl_recursive_lock_t *lock);
+extern int glthread_recursive_lock_lock (gl_recursive_lock_t *lock);
+extern int glthread_recursive_lock_unlock (gl_recursive_lock_t *lock);
+extern int glthread_recursive_lock_destroy (gl_recursive_lock_t *lock);
+
+/* -------------------------- gl_once_t datatype -------------------------- */
+
+typedef once_flag gl_once_t;
+# define gl_once_define(STORAGECLASS, NAME) \
+ STORAGECLASS once_flag NAME = ONCE_FLAG_INIT;
+# define glthread_once(ONCE_CONTROL, INITFUNCTION) \
+ (call_once (ONCE_CONTROL, INITFUNCTION), 0)
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
/* Use the POSIX threads library. */
@@ -502,7 +611,7 @@ typedef glwthread_once_t gl_once_t;
/* ========================================================================= */
-#if !(USE_POSIX_THREADS || USE_WINDOWS_THREADS)
+#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
/* Provide dummy implementation if threads are not supported. */
diff --git a/lib/glthread/thread.c b/lib/glthread/thread.c
index 776bfb6..18e7b23 100644
--- a/lib/glthread/thread.c
+++ b/lib/glthread/thread.c
@@ -28,7 +28,163 @@
/* ========================================================================= */
-#if USE_POSIX_THREADS
+#if USE_ISOC_THREADS
+
+struct thrd_with_exitvalue
+{
+ thrd_t volatile tid;
+ void * volatile exitvalue;
+};
+
+/* The Thread-Specific Storage (TSS) key that allows to access each thread's
+ 'struct thrd_with_exitvalue *' pointer. */
+static tss_t thrd_with_exitvalue_key;
+
+/* Initializes thrd_with_exitvalue_key.
+ This function must only be called once. */
+static void
+do_init_thrd_with_exitvalue_key (void)
+{
+ if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success)
+ abort ();
+}
+
+/* Initializes thrd_with_exitvalue_key. */
+static void
+init_thrd_with_exitvalue_key (void)
+{
+ static once_flag once = ONCE_FLAG_INIT;
+ call_once (&once, do_init_thrd_with_exitvalue_key);
+}
+
+typedef union
+ {
+ struct thrd_with_exitvalue t;
+ struct
+ {
+ thrd_t tid; /* reserve memory for t.tid */
+ void *(*mainfunc) (void *);
+ void *arg;
+ } a;
+ }
+ main_arg_t;
+
+static int
+thrd_main_func (void *pmarg)
+{
+ /* Unpack the object that combines mainfunc and arg. */
+ main_arg_t *main_arg = (main_arg_t *) pmarg;
+ void *(*mainfunc) (void *) = main_arg->a.mainfunc;
+ void *arg = main_arg->a.arg;
+
+ if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success)
+ abort ();
+
+ /* Execute mainfunc, with arg as argument. */
+ {
+ void *exitvalue = mainfunc (arg);
+ /* Store the exitvalue, for use by glthread_join(). */
+ main_arg->t.exitvalue = exitvalue;
+ return 0;
+ }
+}
+
+int
+glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg)
+{
+ init_thrd_with_exitvalue_key ();
+ {
+ /* Combine mainfunc and arg in a single object.
+ A stack-allocated object does not work, because it would be out of
+ existence when thrd_create returns before thrd_main_func is
+ entered. So, allocate it in the heap. */
+ main_arg_t *main_arg = (main_arg_t *) malloc (sizeof (main_arg_t));
+ if (main_arg == NULL)
+ return ENOMEM;
+ main_arg->a.mainfunc = mainfunc;
+ main_arg->a.arg = arg;
+ switch (thrd_create ((thrd_t *) &main_arg->t.tid, thrd_main_func, main_arg))
+ {
+ case thrd_success:
+ break;
+ case thrd_nomem:
+ free (main_arg);
+ return ENOMEM;
+ default:
+ free (main_arg);
+ return EAGAIN;
+ }
+ *threadp = &main_arg->t;
+ return 0;
+ }
+}
+
+gl_thread_t
+gl_thread_self (void)
+{
+ init_thrd_with_exitvalue_key ();
+ {
+ gl_thread_t thread =
+ (struct thrd_with_exitvalue *) tss_get (thrd_with_exitvalue_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 thrd_with_exitvalue *)
+ malloc (sizeof (struct thrd_with_exitvalue));
+ if (thread != NULL)
+ break;
+ /* Memory allocation failed. There is not much we can do. Have to
+ busy-loop, waiting for the availability of memory. */
+ {
+ struct timespec ts;
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ thrd_sleep (&ts, NULL);
+ }
+ }
+ thread->tid = thrd_current ();
+ thread->exitvalue = NULL; /* just to be deterministic */
+ if (tss_set (thrd_with_exitvalue_key, thread) != thrd_success)
+ abort ();
+ }
+ return thread;
+ }
+}
+
+int
+glthread_join (gl_thread_t thread, void **return_value_ptr)
+{
+ /* On Solaris 11.4, thrd_join crashes when the second argument we pass is
+ NULL. */
+ int dummy;
+
+ if (thread == gl_thread_self ())
+ return EINVAL;
+ if (thrd_join (thread->tid, &dummy) != thrd_success)
+ return EINVAL;
+ if (return_value_ptr != NULL)
+ *return_value_ptr = thread->exitvalue;
+ free (thread);
+ return 0;
+}
+
+_Noreturn void
+gl_thread_exit (void *return_value)
+{
+ gl_thread_t thread = gl_thread_self ();
+ thread->exitvalue = return_value;
+ thrd_exit (0);
+}
+
+#endif
+
+/* ========================================================================= */
+
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
#include
diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h
index 2c72cc5..c68a99b 100644
--- a/lib/glthread/thread.h
+++ b/lib/glthread/thread.h
@@ -93,7 +93,39 @@ _GL_INLINE_HEADER_BEGIN
/* ========================================================================= */
-#if USE_POSIX_THREADS
+#if USE_ISOC_THREADS
+
+/* Use the ISO C threads library. */
+
+# include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* -------------------------- gl_thread_t datatype -------------------------- */
+
+typedef struct thrd_with_exitvalue *gl_thread_t;
+extern int glthread_create (gl_thread_t *threadp,
+ void *(*func) (void *), void *arg);
+# define glthread_sigmask(HOW, SET, OSET) \
+ pthread_sigmask (HOW, SET, OSET)
+extern int glthread_join (gl_thread_t thread, void **return_value_ptr);
+extern gl_thread_t gl_thread_self (void);
+# define gl_thread_self_pointer() \
+ (void *) gl_thread_self ()
+extern _Noreturn void gl_thread_exit (void *return_value);
+# define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+/* ========================================================================= */
+
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
/* Use the POSIX threads library. */
@@ -258,7 +290,7 @@ typedef glwthread_thread_t gl_thread_t;
/* ========================================================================= */
-#if !(USE_POSIX_THREADS || USE_WINDOWS_THREADS)
+#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
/* Provide dummy implementation if threads are not supported. */
diff --git a/lib/glthread/threadlib.c b/lib/glthread/threadlib.c
index bca14d9..4455ffc 100644
--- a/lib/glthread/threadlib.c
+++ b/lib/glthread/threadlib.c
@@ -20,7 +20,7 @@
/* ========================================================================= */
-#if USE_POSIX_THREADS
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
/* Use the POSIX threads library. */
diff --git a/lib/glthread/tls.c b/lib/glthread/tls.c
index d182266..90a50e9 100644
--- a/lib/glthread/tls.c
+++ b/lib/glthread/tls.c
@@ -22,6 +22,12 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
#endif
diff --git a/lib/glthread/tls.h b/lib/glthread/tls.h
index cfc9606..32775fc 100644
--- a/lib/glthread/tls.h
+++ b/lib/glthread/tls.h
@@ -58,6 +58,28 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library. */
+
+# include
+
+/* ------------------------- gl_tls_key_t datatype ------------------------- */
+
+typedef tss_t gl_tls_key_t;
+# define glthread_tls_key_init(KEY, DESTRUCTOR) \
+ (tss_create (KEY, DESTRUCTOR) != thrd_success ? EAGAIN : 0)
+# define gl_tls_get(NAME) \
+ tss_get (NAME)
+# define glthread_tls_set(KEY, POINTER) \
+ (tss_set (*(KEY), (POINTER)) != thrd_success ? ENOMEM : 0)
+# define glthread_tls_key_destroy(KEY) \
+ (tss_delete (*(KEY)), 0)
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
/* Use the POSIX threads library. */
@@ -149,7 +171,7 @@ typedef glwthread_tls_key_t gl_tls_key_t;
/* ========================================================================= */
-#if !(USE_POSIX_THREADS || USE_WINDOWS_THREADS)
+#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
/* Provide dummy implementation if threads are not supported. */
diff --git a/lib/glthread/yield.h b/lib/glthread/yield.h
index 27bca55..a707bf5 100644
--- a/lib/glthread/yield.h
+++ b/lib/glthread/yield.h
@@ -25,6 +25,27 @@
/* ========================================================================= */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library. */
+
+# include
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+# define gl_thread_yield() \
+ thrd_yield ()
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+/* ========================================================================= */
+
#if USE_POSIX_THREADS
/* Use the POSIX threads library. */
@@ -66,7 +87,7 @@ extern "C" {
/* ========================================================================= */
-#if !(USE_POSIX_THREADS || USE_WINDOWS_THREADS)
+#if !(USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS)
/* Provide dummy implementation if threads are not supported. */
diff --git a/m4/threadlib.m4 b/m4/threadlib.m4
index 045d9da..7972cfa 100644
--- a/m4/threadlib.m4
+++ b/m4/threadlib.m4
@@ -1,4 +1,4 @@
-# threadlib.m4 serial 20
+# threadlib.m4 serial 21
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,
@@ -16,7 +16,13 @@ dnl (it must be placed before the invocation of gl_THREADLIB_EARLY!), then the
dnl default is 'no', otherwise it is system dependent. In both cases, the user
dnl can change the choice through the options --enable-threads=choice or
dnl --disable-threads.
-dnl Defines at most one of the macros USE_POSIX_THREADS, USE_WINDOWS_THREADS.
+dnl Defines at most one of the macros USE_ISOC_THREADS, USE_POSIX_THREADS,
+dnl USE_ISOC_AND_POSIX_THREADS, USE_WINDOWS_THREADS.
+dnl The choice --enable-threads=isoc+posix is available only on platforms that
+dnl have both the ISO C and the POSIX threads APIs. It has the effect of using
+dnl the ISO C API for most things and the POSIX API only for creating and
+dnl controlling threads (because there is no equivalent to pthread_atfork in
+dnl the ISO C API).
dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use
dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with
dnl libtool).
@@ -54,7 +60,7 @@ AC_DEFUN([gl_THREADLIB_EARLY_BODY],
[m4_divert_text([DEFAULTS], [gl_use_threads_default=])])
m4_divert_text([DEFAULTS], [gl_use_winpthreads_default=])
AC_ARG_ENABLE([threads],
-AC_HELP_STRING([--enable-threads={posix|windows}], [specify multithreading API])m4_ifdef([gl_THREADLIB_DEFAULT_NO], [], [
+AC_HELP_STRING([--enable-threads={isoc|posix|isoc+posix|windows}], [specify multithreading API])m4_ifdef([gl_THREADLIB_DEFAULT_NO], [], [
AC_HELP_STRING([--disable-threads], [build without multithread safety])]),
[gl_use_threads=$enableval],
[if test -n "$gl_use_threads_default"; then
@@ -88,8 +94,11 @@ changequote(,)dnl
changequote([,])dnl
fi
])
- if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
- # For using :
+ if test "$gl_use_threads" = yes \
+ || test "$gl_use_threads" = isoc \
+ || test "$gl_use_threads" = posix \
+ || test "$gl_use_threads" = isoc+posix; then
+ # For using or :
case "$host_os" in
osf*)
# On OSF/1, the compiler needs the flag -D_REENTRANT so that it
@@ -169,7 +178,26 @@ int main ()
AC_CHECK_HEADERS_ONCE([threads.h])
:
fi
- if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ if test "$gl_use_threads" = isoc || test "$gl_use_threads" = isoc+posix; then
+ AC_CHECK_HEADERS_ONCE([threads.h])
+ if test $ac_cv_header_threads_h = yes; then
+ gl_have_isoc_threads=
+ # Test whether both mtx_lock and cnd_timedwait exist in libc.
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include
+ #include
+ mtx_t m;
+ cnd_t c;
+ ]],
+ [[mtx_lock (&m);
+ cnd_timedwait (&c, &m, NULL);]])],
+ [gl_have_isoc_threads=yes])
+ fi
+ fi
+ if test "$gl_use_threads" = yes \
+ || test "$gl_use_threads" = posix \
+ || test "$gl_use_threads" = isoc+posix; then
# On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that
# it groks . It's added above, in gl_THREADLIB_EARLY_BODY.
AC_CHECK_HEADER([pthread.h],
@@ -238,21 +266,34 @@ int main ()
fi
fi
if test -n "$gl_have_pthread"; then
- gl_threads_api=posix
- AC_DEFINE([USE_POSIX_THREADS], [1],
- [Define if the POSIX multithreading library can be used.])
- if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
- if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then
- AC_DEFINE([USE_POSIX_THREADS_WEAK], [1],
- [Define if references to the POSIX multithreading library should be made weak.])
- LIBTHREAD=
- LTLIBTHREAD=
+ if test "$gl_use_threads" = isoc+posix && test "$gl_have_isoc_threads" = yes; then
+ gl_threads_api='isoc+posix'
+ AC_DEFINE([USE_ISOC_AND_POSIX_THREADS], [1],
+ [Define if the combination of the ISO C and POSIX multithreading APIs can be used.])
+ LIBTHREAD= LTLIBTHREAD=
+ else
+ gl_threads_api=posix
+ AC_DEFINE([USE_POSIX_THREADS], [1],
+ [Define if the POSIX multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if case "$gl_cv_have_weak" in *yes) true;; *) false;; esac; then
+ AC_DEFINE([USE_POSIX_THREADS_WEAK], [1],
+ [Define if references to the POSIX multithreading library should be made weak.])
+ LIBTHREAD= LTLIBTHREAD=
+ fi
fi
fi
fi
fi
fi
- if test -z "$gl_have_pthread"; then
+ if test $gl_threads_api = none; then
+ if test "$gl_use_threads" = isoc && test "$gl_have_isoc_threads" = yes; then
+ gl_threads_api=isoc
+ AC_DEFINE([USE_ISOC_THREADS], [1],
+ [Define if the ISO C multithreading library can be used.])
+ fi
+ fi
+ if test $gl_threads_api = none; then
case "$gl_use_threads" in
yes | windows | win32) # The 'win32' is for backward compatibility.
if { case "$host_os" in
diff --git a/tests/test-cond.c b/tests/test-cond.c
index 794fb2d..f1374f4 100644
--- a/tests/test-cond.c
+++ b/tests/test-cond.c
@@ -16,7 +16,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Which tests to perform.
Uncomment some of these, to verify that all tests crash if no locking
diff --git a/tests/test-lock.c b/tests/test-lock.c
index 844e5d5..1ea8100 100644
--- a/tests/test-lock.c
+++ b/tests/test-lock.c
@@ -18,11 +18,17 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS
+# define TEST_ISOC_THREADS 1
+#endif
#if USE_POSIX_THREADS
# define TEST_POSIX_THREADS 1
#endif
+#if USE_ISOC_AND_POSIX_THREADS
+# define TEST_ISOC_AND_POSIX_THREADS 1
+#endif
#if USE_WINDOWS_THREADS
# define TEST_WINDOWS_THREADS 1
#endif
@@ -83,15 +89,23 @@
#include
#if !ENABLE_LOCKING
+# undef USE_ISOC_THREADS
# undef USE_POSIX_THREADS
+# undef USE_ISOC_AND_POSIX_THREADS
# undef USE_WINDOWS_THREADS
#endif
#include "glthread/lock.h"
#if !ENABLE_LOCKING
+# if TEST_ISOC_THREADS
+# define USE_ISOC_THREADS 1
+# endif
# if TEST_POSIX_THREADS
# define USE_POSIX_THREADS 1
# endif
+# if TEST_ISOC_AND_POSIX_THREADS
+# define USE_ISOC_AND_POSIX_THREADS 1
+# endif
# if TEST_WINDOWS_THREADS
# define USE_WINDOWS_THREADS 1
# endif
diff --git a/tests/test-pthread-cond.c b/tests/test-pthread-cond.c
index 201b1e1..79fc2dc 100644
--- a/tests/test-pthread-cond.c
+++ b/tests/test-pthread-cond.c
@@ -16,7 +16,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Which tests to perform.
Uncomment some of these, to verify that all tests crash if no locking
diff --git a/tests/test-pthread-mutex.c b/tests/test-pthread-mutex.c
index 3ea0df6..b7aad02 100644
--- a/tests/test-pthread-mutex.c
+++ b/tests/test-pthread-mutex.c
@@ -18,7 +18,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Whether to enable locking.
Uncomment this to get a test program without locking, to verify that
diff --git a/tests/test-pthread-once2.c b/tests/test-pthread-once2.c
index 101ee52..6a19c57 100644
--- a/tests/test-pthread-once2.c
+++ b/tests/test-pthread-once2.c
@@ -18,7 +18,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Whether to enable locking.
Uncomment this to get a test program without locking, to verify that
diff --git a/tests/test-pthread-rwlock.c b/tests/test-pthread-rwlock.c
index 5d77106..c956f6d 100644
--- a/tests/test-pthread-rwlock.c
+++ b/tests/test-pthread-rwlock.c
@@ -18,7 +18,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Whether to enable locking.
Uncomment this to get a test program without locking, to verify that
diff --git a/tests/test-pthread-tss.c b/tests/test-pthread-tss.c
index fe13a2d..a421a94 100644
--- a/tests/test-pthread-tss.c
+++ b/tests/test-pthread-tss.c
@@ -18,7 +18,7 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
/* Whether to help the scheduler through explicit sched_yield().
Uncomment this to see if the operating system has a fair scheduler. */
diff --git a/tests/test-pthread_sigmask2.c b/tests/test-pthread_sigmask2.c
index 0e5d4dd..7209014 100644
--- a/tests/test-pthread_sigmask2.c
+++ b/tests/test-pthread_sigmask2.c
@@ -27,7 +27,7 @@
#include "macros.h"
-#if USE_POSIX_THREADS
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
static pthread_t main_thread;
static pthread_t killer_thread;
diff --git a/tests/test-thread_create.c b/tests/test-thread_create.c
index 0465b2b..baaa5d1 100644
--- a/tests/test-thread_create.c
+++ b/tests/test-thread_create.c
@@ -67,7 +67,7 @@ main ()
}
else
{
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
fputs ("glthread_create failed\n", stderr);
return 1;
#else
diff --git a/tests/test-tls.c b/tests/test-tls.c
index 9babfe6..d08c605 100644
--- a/tests/test-tls.c
+++ b/tests/test-tls.c
@@ -18,8 +18,11 @@
#include
-#if USE_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+# define TEST_ISOC_THREADS 1
+#endif
#if USE_POSIX_THREADS
# define TEST_POSIX_THREADS 1
#endif
--
2.7.4