]> Savannah Git Hosting - gnulib.git/commitdiff
New options --enable-threads=isoc and --enable-threads=isoc+posix.
authorBruno Haible <bruno@clisp.org>
Wed, 27 Nov 2019 05:03:21 +0000 (06:03 +0100)
committerBruno Haible <bruno@clisp.org>
Wed, 27 Nov 2019 05:03:21 +0000 (06:03 +0100)
* 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.

22 files changed:
ChangeLog
lib/glthread/cond.c
lib/glthread/cond.h
lib/glthread/lock.c
lib/glthread/lock.h
lib/glthread/thread.c
lib/glthread/thread.h
lib/glthread/threadlib.c
lib/glthread/tls.c
lib/glthread/tls.h
lib/glthread/yield.h
m4/threadlib.m4
tests/test-cond.c
tests/test-lock.c
tests/test-pthread-cond.c
tests/test-pthread-mutex.c
tests/test-pthread-once2.c
tests/test-pthread-rwlock.c
tests/test-pthread-tss.c
tests/test-pthread_sigmask2.c
tests/test-thread_create.c
tests/test-tls.c

index d612dce92134c64c3fed977115bfb4b638906512..bd2330e95ec03a15b93e28d7433a57a79604791b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,40 @@
+2019-11-27  Bruno Haible  <bruno@clisp.org>
+
+       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  <bruno@clisp.org>
 
        mbrtowc: Modernize autoconf test.
index f6a06cc9fafdd26fe7ec3601973a049a8a43fd0d..df166bfe4b683859162a49ca7ebdee571c6778b9 100644 (file)
 
 /* ========================================================================= */
 
+#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
index 4c6f4d94c09156113c8f2907971b38c7b0ecf2b0..cd1ec98ec8c0d617e9350fd7ff2e46ee348a9dc3 100644 (file)
@@ -76,6 +76,53 @@ _GL_INLINE_HEADER_BEGIN
 
 /* ========================================================================= */
 
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library.  */
+
+# include <threads.h>
+
+# 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.  */
 
index 852d84d3bb6a0ad5d9c48d819caf5c373375e85b..2537839de55af2fdecb12fd351f03769f69c91d3 100644 (file)
 
 /* ========================================================================= */
 
+#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 -------------------------- */
index 78dd67f29f3370c7eacf34615853ecf37979d5fb..ffbec15143b68ac812adb113f80a58d65ee2caad 100644 (file)
 
 /* ========================================================================= */
 
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library.  */
+
+# include <threads.h>
+
+# 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.  */
 
index 776bfb6bf4cd4c430a2ddf31075f71ae5ab0871f..18e7b237d6716f6843604313e63cbf1897c2bb20 100644 (file)
 
 /* ========================================================================= */
 
-#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 <pthread.h>
 
index 2c72cc5cdb2ea6e7b0a9f75902e7473f486e07af..c68a99b648e64b160eb16f24d62a9651a3bfe395 100644 (file)
@@ -93,7 +93,39 @@ _GL_INLINE_HEADER_BEGIN
 
 /* ========================================================================= */
 
-#if USE_POSIX_THREADS
+#if USE_ISOC_THREADS
+
+/* Use the ISO C threads library.  */
+
+# include <threads.h>
+
+# 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.  */
 
index bca14d9739f1a1e54bc26f995971bfba39099d64..4455ffc9298bd87d09d22c5dbf5b96495bb95b6b 100644 (file)
@@ -20,7 +20,7 @@
 
 /* ========================================================================= */
 
-#if USE_POSIX_THREADS
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
 
 /* Use the POSIX threads library.  */
 
index d182266c569139aba98af6a69cbde168873e6b5f..90a50e9e69ff3ad8a277399a24f79995b9095564 100644 (file)
 
 /* ========================================================================= */
 
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+#endif
+
+/* ========================================================================= */
+
 #if USE_POSIX_THREADS
 
 #endif
index cfc96061c10c942138dc89a2fea92db37b400b49..32775fc6bc48a57bf1510248c14ff7f329e7a61c 100644 (file)
 
 /* ========================================================================= */
 
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library.  */
+
+# include <threads.h>
+
+/* ------------------------- 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.  */
 
index 27bca55b797f490cf36b08937d6d6a641d45475c..a707bf5e62cd80a235b4d3f4ec2e02b9e97c0033 100644 (file)
 
 /* ========================================================================= */
 
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+/* Use the ISO C threads library.  */
+
+# include <threads.h>
+
+# 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.  */
 
index 045d9dad29c8446acaddf81deb5362bd4fac9a0b..7972cfaebcda8fda1710dae97440f089c0026b3e 100644 (file)
@@ -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 <pthread.h>:
+  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 <threads.h> or <pthread.h>:
     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 <threads.h>
+               #include <stddef.h>
+               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 <pthread.h>. 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
index 794fb2d314abed24d19b819b7f76f4e7b3dd289f..f1374f43509a775956821dd11c686f68d1a70e43 100644 (file)
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#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
index 844e5d52e3c85065f5cc238979445e4aebb40a2d..1ea81008cd2bb70e72d388b3a8c5e125e2c38a0e 100644 (file)
 
 #include <config.h>
 
-#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
 #include <string.h>
 
 #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
index 201b1e15ba0c94d43f7fc53f8994f2ee4a5a6e66..79fc2dcfc6808cd8b5b55a68b90d0e4a18004752 100644 (file)
@@ -16,7 +16,7 @@
 
 #include <config.h>
 
-#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
index 3ea0df6edcda55a051536ab2f98356f1a48d00cc..b7aad02ef1b7bebcdd68330fa2f93aa0f4cf281a 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#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
index 101ee52f2b65d1882297bc2ddef0f28c922c3e89..6a19c57f2fd85c3c0d4cd94728aa30f460e8699d 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#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
index 5d771060e7565b8e3dae413ad9494edaf3dbc0b5..c956f6dc9d31b93b5b8f04ee1897ed9a87317dcc 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#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
index fe13a2d0d8b3bce1e3e563fff0b956370df9cf46..a421a947b7fcf6ac9dbf9183625d2008751d55a4 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <config.h>
 
-#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.  */
index 0e5d4dda0fd771c691156f599c86251fec5c6c5c..72090141317e4614866036ad4434c8c106be85c0 100644 (file)
@@ -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;
index 0465b2ba8198c8028fdf08f6fdbe7ab341160223..baaa5d1679720edbd62651878d1d16d6e91dc2ec 100644 (file)
@@ -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
index 9babfe6ad1934950e3ca44f55b8114756e9098b6..d08c605e14bbd36a3283015c74e5c0d15ac8f2ed 100644 (file)
 
 #include <config.h>
 
-#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