>From 0894f96f89e0e44bdf2921e6cd51fca429bf9802 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Thu, 20 Jun 2019 04:16:20 +0200 Subject: [PATCH 15/26] 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. --- ChangeLog | 22 +++++ lib/glthread/thread.c | 182 ------------------------------------- lib/glthread/thread.h | 25 ++---- lib/windows-thread.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/windows-thread.h | 52 +++++++++++ modules/thread | 1 + modules/windows-thread | 28 ++++++ 7 files changed, 347 insertions(+), 200 deletions(-) create mode 100644 lib/windows-thread.c create mode 100644 lib/windows-thread.h create mode 100644 modules/windows-thread diff --git a/ChangeLog b/ChangeLog index 33262df..ab97f37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,27 @@ 2019-06-20 Bruno Haible + 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. + +2019-06-20 Bruno Haible + windows-tls: New module. * lib/windows-tls.h: New file, based on lib/glthread/tls.h. * lib/windows-tls.c: New file, based on lib/glthread/tls.h. diff --git a/lib/glthread/thread.c b/lib/glthread/thread.c index 9461949..9da0542 100644 --- a/lib/glthread/thread.c +++ b/lib/glthread/thread.c @@ -45,188 +45,6 @@ const gl_thread_t gl_null_thread /* = { .p = NULL } */; #if USE_WINDOWS_THREADS -#include - -/* -------------------------- gl_thread_t datatype -------------------------- */ - -/* The Thread-Local Storage (TLS) key that allows to access each thread's - 'struct gl_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) -{ - gl_once_define(static, once) - gl_once (once, do_init_self_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; -}; - -/* 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; -} - -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; -} - -int -glthread_create_func (gl_thread_t *threadp, void * (*func) (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; - - { - 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 -glthread_join_func (gl_thread_t thread, void **retvalp) -{ - if (thread == NULL) - return EINVAL; - - 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); - free (thread); - - return 0; -} - -int -gl_thread_exit_func (void *retval) -{ - gl_thread_t thread = gl_thread_self (); - thread->result = retval; - _endthreadex (0); /* calls ExitThread (0) */ - abort (); -} - #endif /* ========================================================================= */ diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h index f263129..bf4e74a 100644 --- a/lib/glthread/thread.h +++ b/lib/glthread/thread.h @@ -336,39 +336,28 @@ typedef thread_t gl_thread_t; # define WIN32_LEAN_AND_MEAN /* avoid including junk */ # include +# include "windows-thread.h" + # ifdef __cplusplus extern "C" { # endif /* -------------------------- gl_thread_t datatype -------------------------- */ -/* The gl_thread_t is a pointer to a structure in memory. - Why not the thread handle? If it were the thread handle, it would be hard - to implement gl_thread_self() (since GetCurrentThread () returns a pseudo- - handle, DuplicateHandle (GetCurrentThread ()) returns a handle that must be - closed afterwards, and there is no function for quickly retrieving a thread - handle from its id). - Why not the thread id? I tried it. It did not work: Sometimes ids appeared - that did not belong to running threads, and glthread_join failed with ESRCH. - */ -typedef struct gl_thread_struct *gl_thread_t; +typedef glwthread_thread_t gl_thread_t; # define glthread_create(THREADP, FUNC, ARG) \ - glthread_create_func (THREADP, FUNC, ARG) + glwthread_thread_create (THREADP, FUNC, ARG) # define glthread_sigmask(HOW, SET, OSET) \ /* unsupported */ 0 # define glthread_join(THREAD, RETVALP) \ - glthread_join_func (THREAD, RETVALP) + glwthread_thread_join (THREAD, RETVALP) # define gl_thread_self() \ - gl_thread_self_func () + glwthread_thread_self () # define gl_thread_self_pointer() \ gl_thread_self () # define gl_thread_exit(RETVAL) \ - gl_thread_exit_func (RETVAL) + glwthread_thread_exit (RETVAL) # define glthread_atfork(PREPARE_FUNC, PARENT_FUNC, CHILD_FUNC) 0 -extern int glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg); -extern int glthread_join_func (gl_thread_t thread, void **retvalp); -extern gl_thread_t gl_thread_self_func (void); -extern int gl_thread_exit_func (void *retval); # ifdef __cplusplus } diff --git a/lib/windows-thread.c b/lib/windows-thread.c new file mode 100644 index 0000000..01daa78 --- /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 . */ + +/* Written by Bruno Haible , 2005. + Based on GCC's gthr-win32.h. */ + +#include + +/* Specification. */ +#include "windows-thread.h" + +#include +#include + +#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 (); +} diff --git a/lib/windows-thread.h b/lib/windows-thread.h new file mode 100644 index 0000000..8bf98fb --- /dev/null +++ b/lib/windows-thread.h @@ -0,0 +1,52 @@ +/* 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 . */ + +/* Written by Bruno Haible , 2005. + Based on GCC's gthr-win32.h. */ + +#ifndef _WINDOWS_THREAD_H +#define _WINDOWS_THREAD_H + +#define WIN32_LEAN_AND_MEAN /* avoid including junk */ +#include + +/* The glwthread_thread_t is a pointer to a structure in memory. + Why not the thread handle? If it were the thread handle, it would be hard + to implement glwthread_thread_self() (since GetCurrentThread () returns a + pseudo-handle, DuplicateHandle (GetCurrentThread ()) returns a handle that + must be closed afterwards, and there is no function for quickly retrieving + a thread handle from its id). + Why not the thread id? I tried it. It did not work: Sometimes ids appeared + that did not belong to running threads, and glthread_join failed with ESRCH. + */ +typedef struct glwthread_thread_struct *glwthread_thread_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int glwthread_thread_create (glwthread_thread_t *threadp, + void * (*func) (void *), void *arg); +extern int glwthread_thread_join (glwthread_thread_t thread, void **retvalp); +extern int glwthread_thread_detach (glwthread_thread_t thread); +extern glwthread_thread_t glwthread_thread_self (void); +extern int glwthread_thread_exit (void *retval); + +#ifdef __cplusplus +} +#endif + +#endif /* _WINDOWS_THREAD_H */ diff --git a/modules/thread b/modules/thread index b4e9d78..baa8887 100644 --- a/modules/thread +++ b/modules/thread @@ -10,6 +10,7 @@ Depends-on: threadlib extern-inline lock +windows-thread [test $gl_threads_api = windows] pthread_sigmask [test $gl_threads_api = posix] configure.ac: diff --git a/modules/windows-thread b/modules/windows-thread new file mode 100644 index 0000000..3f35344 --- /dev/null +++ b/modules/windows-thread @@ -0,0 +1,28 @@ +Description: +Creating and controlling threads (native Windows implementation). + +Files: +lib/windows-thread.h +lib/windows-thread.c + +Depends-on: +windows-once + +configure.ac: +AC_REQUIRE([AC_CANONICAL_HOST]) +case "$host_os" in + mingw*) + AC_LIBOBJ([windows-thread]) + ;; +esac + +Makefile.am: + +Include: +"windows-thread.h" + +License: +LGPLv2+ + +Maintainer: +all -- 2.7.4