>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