summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog22
-rw-r--r--lib/glthread/thread.c182
-rw-r--r--lib/glthread/thread.h25
-rw-r--r--lib/windows-thread.c237
-rw-r--r--lib/windows-thread.h52
-rw-r--r--modules/thread1
-rw-r--r--modules/windows-thread28
7 files changed, 347 insertions, 200 deletions
diff --git a/ChangeLog b/ChangeLog
index 33262dfe14..ab97f37093 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
2019-06-20 Bruno Haible <bruno@clisp.org>
+ 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 <bruno@clisp.org>
+
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 9461949911..9da054242b 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 <process.h>
-
-/* -------------------------- 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 f26312998c..bf4e74a5b3 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 <windows.h>
+# 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 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 ();
+}
diff --git a/lib/windows-thread.h b/lib/windows-thread.h
new file mode 100644
index 0000000000..8bf98fbf46
--- /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 <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 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 <windows.h>
+
+/* 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 b4e9d78d61..baa88872ae 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 0000000000..3f353446e6
--- /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