diff options
Diffstat (limited to 'tests/glthread/thread.c')
-rw-r--r-- | tests/glthread/thread.c | 320 |
1 files changed, 152 insertions, 168 deletions
diff --git a/tests/glthread/thread.c b/tests/glthread/thread.c index 0387406b..0b923c04 100644 --- a/tests/glthread/thread.c +++ b/tests/glthread/thread.c @@ -1,27 +1,25 @@ /* Creating and controlling threads. - Copyright (C) 2005-2018 Free Software Foundation, Inc. + Copyright (C) 2005-2022 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3, or (at your option) - any later version. + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Lesser General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, see <https://www.gnu.org/licenses/>. */ + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ /* Written by Bruno Haible <bruno@clisp.org>, 2005. - Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, - gthr-win32.h. */ + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-win32.h. */ #include <config.h> /* Specification. */ -# define _GLTHREAD_THREAD_INLINE _GL_EXTERN_INLINE #include "glthread/thread.h" #include <stdlib.h> @@ -29,204 +27,190 @@ /* ========================================================================= */ -#if USE_POSIX_THREADS +#if USE_ISOC_THREADS -#include <pthread.h> - -#if defined PTW32_VERSION || defined __MVS__ - -const gl_thread_t gl_null_thread /* = { .p = NULL } */; - -#endif - -#endif - -/* ========================================================================= */ - -#if USE_WINDOWS_THREADS - -#include <process.h> - -/* -------------------------- gl_thread_t datatype -------------------------- */ +struct thrd_with_exitvalue +{ + thrd_t volatile tid; + void * volatile exitvalue; +}; -/* The Thread-Local Storage (TLS) key that allows to access each thread's - 'struct gl_thread_struct *' pointer. */ -static DWORD self_key = (DWORD)-1; +/* 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 self_key. This function must only be called once. */ +/* Initializes thrd_with_exitvalue_key. + This function must only be called once. */ static void -do_init_self_key (void) +do_init_thrd_with_exitvalue_key (void) { - self_key = TlsAlloc (); - /* If this fails, we're hosed. */ - if (self_key == (DWORD)-1) + if (tss_create (&thrd_with_exitvalue_key, NULL) != thrd_success) abort (); } -/* Initializes self_key. */ +/* Initializes thrd_with_exitvalue_key. */ static void -init_self_key (void) +init_thrd_with_exitvalue_key (void) { - gl_once_define(static, once) - gl_once (once, do_init_self_key); + static once_flag once = ONCE_FLAG_INIT; + call_once (&once, do_init_thrd_with_exitvalue_key); } -/* This structure contains information about a thread. - It is stored in TLS under key self_key. */ -struct gl_thread_struct -{ - /* Fields for managing the handle. */ - HANDLE volatile handle; - CRITICAL_SECTION handle_lock; - /* Fields for managing the exit value. */ - void * volatile result; - /* Fields for managing the thread start. */ - void * (*func) (void *); - void *arg; -}; +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; -/* Return a real HANDLE object for the current thread. */ -static HANDLE -get_current_thread_handle (void) +static int +thrd_main_func (void *pmarg) { - HANDLE this_handle; + /* 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; - /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic - identifier, not a real handle. */ - if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), - GetCurrentProcess (), &this_handle, - 0, FALSE, DUPLICATE_SAME_ACCESS)) + if (tss_set (thrd_with_exitvalue_key, &main_arg->t) != thrd_success) abort (); - return this_handle; -} - -gl_thread_t -gl_thread_self_func (void) -{ - gl_thread_t thread; - - if (self_key == (DWORD)-1) - init_self_key (); - thread = TlsGetValue (self_key); - if (thread == NULL) - { - /* This happens only in threads that have not been created through - glthread_create(), such as the main thread. */ - for (;;) - { - thread = - (struct gl_thread_struct *) - malloc (sizeof (struct gl_thread_struct)); - if (thread != NULL) - break; - /* Memory allocation failed. There is not much we can do. Have to - busy-loop, waiting for the availability of memory. */ - Sleep (1); - } - - thread->handle = get_current_thread_handle (); - InitializeCriticalSection (&thread->handle_lock); - thread->result = NULL; /* just to be deterministic */ - TlsSetValue (self_key, thread); - } - return thread; -} -/* The main function of a freshly creating thread. It's a wrapper around - the FUNC and ARG arguments passed to glthread_create_func. */ -static unsigned int WINAPI -wrapper_func (void *varg) -{ - struct gl_thread_struct *thread = (struct gl_thread_struct *)varg; - - EnterCriticalSection (&thread->handle_lock); - /* Create a new handle for the thread only if the parent thread did not yet - fill in the handle. */ - if (thread->handle == NULL) - thread->handle = get_current_thread_handle (); - LeaveCriticalSection (&thread->handle_lock); - - if (self_key == (DWORD)-1) - init_self_key (); - TlsSetValue (self_key, thread); - - /* Run the thread. Store the exit value if the thread was not terminated - otherwise. */ - thread->result = thread->func (thread->arg); - return 0; + /* 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_func (gl_thread_t *threadp, void * (*func) (void *), void *arg) +glthread_create (gl_thread_t *threadp, void *(*mainfunc) (void *), void *arg) { - struct gl_thread_struct *thread = - (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct)); - if (thread == NULL) - return ENOMEM; - thread->handle = NULL; - InitializeCriticalSection (&thread->handle_lock); - thread->result = NULL; /* just to be deterministic */ - thread->func = func; - thread->arg = arg; - + init_thrd_with_exitvalue_key (); { - unsigned int thread_id; - HANDLE thread_handle; - - thread_handle = (HANDLE) - _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id); - /* calls CreateThread with the same arguments */ - if (thread_handle == NULL) + /* 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)) { - DeleteCriticalSection (&thread->handle_lock); - free (thread); + case thrd_success: + break; + case thrd_nomem: + free (main_arg); + return ENOMEM; + default: + free (main_arg); return EAGAIN; } - - EnterCriticalSection (&thread->handle_lock); - if (thread->handle == NULL) - thread->handle = thread_handle; - else - /* thread->handle was already set by the thread itself. */ - CloseHandle (thread_handle); - LeaveCriticalSection (&thread->handle_lock); - - *threadp = thread; + *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_func (gl_thread_t thread, void **retvalp) +glthread_join (gl_thread_t thread, void **return_value_ptr) { - if (thread == NULL) - return EINVAL; + /* On Solaris 11.4, thrd_join crashes when the second argument we pass is + NULL. */ + int dummy; if (thread == gl_thread_self ()) - return EDEADLK; - - if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED) return EINVAL; - - if (retvalp != NULL) - *retvalp = thread->result; - - DeleteCriticalSection (&thread->handle_lock); - CloseHandle (thread->handle); + if (thrd_join (thread->tid, &dummy) != thrd_success) + return EINVAL; + if (return_value_ptr != NULL) + *return_value_ptr = thread->exitvalue; free (thread); - return 0; } -int -gl_thread_exit_func (void *retval) +_Noreturn void +gl_thread_exit (void *return_value) { gl_thread_t thread = gl_thread_self (); - thread->result = retval; - _endthreadex (0); /* calls ExitThread (0) */ - abort (); + thread->exitvalue = return_value; + thrd_exit (0); } #endif /* ========================================================================= */ + +#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS + +#include <pthread.h> + +#if defined PTW32_VERSION || defined __MVS__ + +const gl_thread_t gl_null_thread /* = { .p = NULL } */; + +#endif + +#endif + +/* ========================================================================= */ + +#if USE_WINDOWS_THREADS + +#endif + +/* ========================================================================= */ + +gl_thread_t +gl_thread_create (void *(*func) (void *arg), void *arg) +{ + gl_thread_t thread; + int ret; + + ret = glthread_create (&thread, func, arg); + if (ret != 0) + abort (); + return thread; +} |