diff options
author | Bruno Haible <bruno@clisp.org> | 2019-06-20 04:16:20 +0200 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2019-06-20 04:17:11 +0200 |
commit | 0894f96f89e0e44bdf2921e6cd51fca429bf9802 (patch) | |
tree | 24ba548ed51d2b75af2299102410646764738c67 /lib/windows-thread.c | |
parent | 7435d61f26ef6dece4f94ea00972145b587ef7ab (diff) | |
download | gnulib-0894f96f89e0e44bdf2921e6cd51fca429bf9802.tar.gz |
windows-thread: New module.
* lib/windows-thread.h: New file, based on lib/glthread/thread.h.
* lib/windows-thread.c: New file, based on lib/glthread/thread.c.
* lib/glthread/thread.h: Include windows-thread.h.
(gl_thread_t): Define using glwthread_thread_t.
(glthread_create): Define using glwthread_thread_create.
(glthread_join): Define using glwthread_thread_join.
(gl_thread_self): Define using glwthread_thread_self.
(gl_thread_exit): Define using glwthread_thread_exit.
(glthread_create_func, glthread_join_func, gl_thread_self_func,
gl_thread_exit_func): Remove declarations.
* lib/glthread/thread.c (self_key): Remove variable.
(do_init_self_key, init_self_key): Remove functions.
(struct gl_thread_struct): Remove type.
(get_current_thread_handle, gl_thread_self_func, wrapper_func,
glthread_create_func, glthread_join_func, gl_thread_exit_func): Remove
functions.
* modules/windows-thread: New file.
* modules/thread (Depends-on): Add windows-thread.
Diffstat (limited to 'lib/windows-thread.c')
-rw-r--r-- | lib/windows-thread.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/lib/windows-thread.c b/lib/windows-thread.c new file mode 100644 index 0000000000..01daa78a14 --- /dev/null +++ b/lib/windows-thread.c @@ -0,0 +1,237 @@ +/* Creating and controlling threads (native Windows implementation). + Copyright (C) 2005-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2005. + Based on GCC's gthr-win32.h. */ + +#include <config.h> + +/* Specification. */ +#include "windows-thread.h" + +#include <process.h> +#include <stdlib.h> + +#include "windows-once.h" + +/* The Thread-Local Storage (TLS) key that allows to access each thread's + 'struct glwthread_thread_struct *' pointer. */ +static DWORD self_key = (DWORD)-1; + +/* Initializes self_key. This function must only be called once. */ +static void +do_init_self_key (void) +{ + self_key = TlsAlloc (); + /* If this fails, we're hosed. */ + if (self_key == (DWORD)-1) + abort (); +} + +/* Initializes self_key. */ +static void +init_self_key (void) +{ + static glwthread_once_t once = GLWTHREAD_ONCE_INIT; + glwthread_once (&once, do_init_self_key); +} + +/* This structure contains information about a thread. + It is stored in TLS under key self_key. */ +struct glwthread_thread_struct +{ + /* Fields for managing the handle. */ + HANDLE volatile handle; + CRITICAL_SECTION handle_lock; + /* Fields for managing the exit value. */ + BOOL volatile detached; + void * volatile result; + /* Fields for managing the thread start. */ + void * (*func) (void *); + void *arg; +}; + +/* Return a real HANDLE object for the current thread. */ +static HANDLE +get_current_thread_handle (void) +{ + HANDLE this_handle; + + /* GetCurrentThread() returns a pseudo-handle, i.e. only a symbolic + identifier, not a real handle. */ + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &this_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + abort (); + return this_handle; +} + +glwthread_thread_t +glwthread_thread_self (void) +{ + glwthread_thread_t thread; + + if (self_key == (DWORD)-1) + init_self_key (); + thread = TlsGetValue (self_key); + if (thread == NULL) + { + /* This happens only in threads that have not been created through + glthread_create(), such as the main thread. */ + for (;;) + { + thread = + (struct glwthread_thread_struct *) + malloc (sizeof (struct glwthread_thread_struct)); + if (thread != NULL) + break; + /* Memory allocation failed. There is not much we can do. Have to + busy-loop, waiting for the availability of memory. */ + Sleep (1); + } + + thread->handle = get_current_thread_handle (); + InitializeCriticalSection (&thread->handle_lock); + thread->detached = FALSE; /* This can lead to a memory leak. */ + thread->result = NULL; /* just to be deterministic */ + TlsSetValue (self_key, thread); + } + return thread; +} + +/* The main function of a freshly creating thread. It's a wrapper around + the FUNC and ARG arguments passed to glthread_create_func. */ +static unsigned int WINAPI +wrapper_func (void *varg) +{ + struct glwthread_thread_struct *thread = + (struct glwthread_thread_struct *) varg; + + EnterCriticalSection (&thread->handle_lock); + /* Create a new handle for the thread only if the parent thread did not yet + fill in the handle. */ + if (thread->handle == NULL) + thread->handle = get_current_thread_handle (); + LeaveCriticalSection (&thread->handle_lock); + + if (self_key == (DWORD)-1) + init_self_key (); + TlsSetValue (self_key, thread); + + /* Run the thread. Store the exit value if the thread was not terminated + otherwise. */ + thread->result = thread->func (thread->arg); + + if (thread->detached) + { + /* Clean up the thread, like thrd_join would do. */ + DeleteCriticalSection (&thread->handle_lock); + CloseHandle (thread->handle); + free (thread); + } + + return 0; +} + +int +glwthread_thread_create (glwthread_thread_t *threadp, + void * (*func) (void *), void *arg) +{ + struct glwthread_thread_struct *thread = + (struct glwthread_thread_struct *) + malloc (sizeof (struct glwthread_thread_struct)); + if (thread == NULL) + return ENOMEM; + thread->handle = NULL; + InitializeCriticalSection (&thread->handle_lock); + thread->detached = FALSE; + thread->result = NULL; /* just to be deterministic */ + thread->func = func; + thread->arg = arg; + + { + unsigned int thread_id; + HANDLE thread_handle; + + thread_handle = (HANDLE) + _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id); + /* calls CreateThread with the same arguments */ + if (thread_handle == NULL) + { + DeleteCriticalSection (&thread->handle_lock); + free (thread); + return EAGAIN; + } + + EnterCriticalSection (&thread->handle_lock); + if (thread->handle == NULL) + thread->handle = thread_handle; + else + /* thread->handle was already set by the thread itself. */ + CloseHandle (thread_handle); + LeaveCriticalSection (&thread->handle_lock); + + *threadp = thread; + return 0; + } +} + +int +glwthread_thread_join (glwthread_thread_t thread, void **retvalp) +{ + if (thread == NULL) + return EINVAL; + + if (thread == glwthread_thread_self ()) + return EDEADLK; + + if (thread->detached) + return EINVAL; + + if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED) + return EINVAL; + + if (retvalp != NULL) + *retvalp = thread->result; + + DeleteCriticalSection (&thread->handle_lock); + CloseHandle (thread->handle); + free (thread); + + return 0; +} + +int +glwthread_thread_detach (glwthread_thread_t thread) +{ + if (thread == NULL) + return EINVAL; + + if (thread->detached) + return EINVAL; + + thread->detached = TRUE; + return 0; +} + +int +glwthread_thread_exit (void *retval) +{ + glwthread_thread_t thread = glwthread_thread_self (); + thread->result = retval; + _endthreadex (0); /* calls ExitThread (0) */ + abort (); +} |