Implement thread control primitives for Win32.
authorBruno Haible <bruno@clisp.org>
Mon, 18 Aug 2008 00:23:32 +0000 (02:23 +0200)
committerBruno Haible <bruno@clisp.org>
Mon, 18 Aug 2008 00:23:32 +0000 (02:23 +0200)
ChangeLog
lib/glthread/thread.c [new file with mode: 0644]
lib/glthread/thread.h
modules/thread

index 7cf29c2..f1c6180 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-08-17  Bruno Haible  <bruno@clisp.org>
+
+       * lib/glthread/thread.c: New file, based on code from tests/test-lock.c.
+       * lib/glthread/thread.h: Provide Win32 specific implementation.
+       * modules/thread (Files): Add lib/glthread/thread.c.
+       (Depends-on): Add lock.
+       (Makefile.am): Add glthread/thread.c to lib_SOURCES.
+
 2008-08-17  Yoann Vandoorselaere  <yoann.v@prelude-ids.com>
 
        New module 'yield'.
diff --git a/lib/glthread/thread.c b/lib/glthread/thread.c
new file mode 100644 (file)
index 0000000..9894b8d
--- /dev/null
@@ -0,0 +1,186 @@
+/* Creating and controlling threads.
+   Copyright (C) 2005-2008 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, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2005.
+   Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h,
+   gthr-win32.h.  */
+
+#include <config.h>
+
+#include "glthread/thread.h"
+
+#include <stdlib.h>
+#include "glthread/lock.h"
+
+/* ========================================================================= */
+
+#if USE_WIN32_THREADS
+
+/* -------------------------- gl_thread_t datatype -------------------------- */
+
+/* Use a wrapper function, instead of adding WINAPI through a cast.
+   This struct also holds the thread's exit value.  */
+struct thread_extra
+  {
+    /* Fields for managing the association between thread id and handle.  */
+    DWORD volatile id;
+    HANDLE volatile handle;
+    struct thread_extra * volatile next;
+    /* Fields for managing the exit value.  */
+    void * volatile result;
+    /* Fields for managing the thread start.  */
+    void * (*func) (void *);
+    void *arg;
+  };
+
+/* Linked list of thread_extra of running or zombie (not-yet-joined)
+   threads.
+   TODO: Use a hash table indexed by id instead of a linked list.  */
+static struct thread_extra *running_threads /* = NULL */;
+
+/* Lock protecting running_threads.  */
+gl_lock_define_initialized(static, running_lock)
+
+static DWORD WINAPI
+wrapper_func (void *varg)
+{
+  struct thread_extra *xarg = (struct thread_extra *)varg;
+
+  xarg->id = GetCurrentThreadId ();
+  /* Add xarg to the list of running thread_extra.  */
+  gl_lock_lock (running_lock);
+  if (!(xarg->id == GetCurrentThreadId ()))
+    abort ();
+  xarg->next = running_threads;
+  running_threads = xarg;
+  gl_lock_unlock (running_lock);
+
+  /* Run the thread.  Store the exit value if the thread was not terminated
+     otherwise.  */
+  xarg->result = xarg->func (xarg->arg);
+  return 0;
+}
+
+int
+glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg)
+{
+  struct thread_extra *x =
+    (struct thread_extra *) malloc (sizeof (struct thread_extra));
+  if (x == NULL)
+    return ENOMEM;
+  x->result = NULL; /* just to be deterministic */
+  x->func = func;
+  x->arg = arg;
+  {
+    DWORD thread_id;
+    HANDLE thread_handle;
+
+    gl_lock_lock (running_lock);
+    thread_handle = CreateThread (NULL, 100000, wrapper_func, x, 0, &thread_id);
+    if (thread_handle == NULL)
+      {
+       gl_lock_unlock (running_lock);
+       return EAGAIN;
+      }
+    x->id = thread_id;
+    x->handle = thread_handle;
+    gl_lock_unlock (running_lock);
+    *threadp = thread_id;
+    return 0;
+  }
+}
+
+int
+glthread_join_func (gl_thread_t thread, void **retvalp)
+{
+  HANDLE thread_handle;
+
+  if (thread == gl_thread_self ())
+    return EDEADLK;
+
+  /* Find the thread handle that corresponds to the thread id.
+     The thread argument must come from either the parent thread or from the
+     thread itself.  So at this point, either glthread_create_func was
+     completed (and x->handle set), or x->func was invoked (and that can
+     only be after the running_lock was acquired, hence after
+     glthread_create_func released it, hence x->handle is set as well).  */
+  thread_handle = NULL;
+  gl_lock_lock (running_lock);
+  {
+    struct thread_extra *x;
+    for (x = running_threads; x != NULL; x = x->next)
+      if (x->id == thread)
+       {
+         thread_handle = x->handle;
+         break;
+       }
+  }
+  gl_lock_unlock (running_lock);
+  if (thread_handle == NULL)
+    return ESRCH;
+
+  if (WaitForSingleObject (thread_handle, INFINITE) == WAIT_FAILED)
+    return EINVAL;
+
+  /* Remove the 'struct thread_extra' from running_threads.  */
+  gl_lock_lock (running_lock);
+  {
+    struct thread_extra **xp;
+    for (xp = &running_threads; *xp != NULL; xp = &(*xp)->next)
+      if ((*xp)->id == thread)
+       {
+         struct thread_extra *x = *xp;
+         if (retvalp != NULL)
+           *retvalp = x->result;
+         if (x->handle != thread_handle)
+           abort ();
+         *xp = x->next;
+         free (x);
+         break;
+       }
+  }
+  gl_lock_unlock (running_lock);
+
+  CloseHandle (thread_handle);
+
+  return 0;
+}
+
+int
+gl_thread_exit_func (void *retval)
+{
+  DWORD this_thread = GetCurrentThreadId ();
+
+  /* Store the exit value in the appropriate element of running_threads.  */
+  gl_lock_lock (running_lock);
+  {
+    struct thread_extra *x;
+    for (x = running_threads; x != NULL; x = x->next)
+      if (x->id == this_thread)
+       {
+         x->result = retval;
+         break;
+       }
+  }
+  gl_lock_unlock (running_lock);
+
+  ExitThread (0);
+}
+
+#endif
+
+/* ========================================================================= */
index 5c98cdd..d88908e 100644 (file)
@@ -267,6 +267,46 @@ typedef thread_t gl_thread_t;
 
 /* ========================================================================= */
 
+#if USE_WIN32_THREADS
+
+# include <windows.h>
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* -------------------------- gl_thread_t datatype -------------------------- */
+
+/* The gl_thread_t is the thread id, 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).  */
+typedef DWORD gl_thread_t;
+# define glthread_create(THREADP, FUNC, ARG) \
+    glthread_create_func (THREADP, FUNC, ARG)
+# define glthread_sigmask(HOW, SET, OSET) \
+    /* unsupported */ 0
+# define glthread_join(THREAD, RETVALP) \
+    glthread_join_func (THREAD, RETVALP)
+# define gl_thread_self() \
+    GetCurrentThreadId ()
+# define gl_thread_exit(RETVAL) \
+    gl_thread_exit_func (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 int gl_thread_exit_func (void *retval);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+/* ========================================================================= */
+
 #if !(USE_POSIX_THREADS || USE_PTH_THREADS || USE_SOLARIS_THREADS || USE_WIN32_THREADS)
 
 /* Provide dummy implementation if threads are not supported.  */
index d9586b5..a7d338d 100644 (file)
@@ -3,16 +3,18 @@ Creating and controlling threads.
 
 Files:
 lib/glthread/thread.h
+lib/glthread/thread.c
 m4/thread.m4
 
 Depends-on:
 threadlib
+lock
 
 configure.ac:
 gl_THREAD
 
 Makefile.am:
-lib_SOURCES += glthread/thread.h 
+lib_SOURCES += glthread/thread.h glthread/thread.c
 
 Include:
 "glthread/thread.h"