mbsinit: Work around mingw bug.
[gnulib.git] / lib / glthread / thread.c
index 9894b8d..6b134a7 100644 (file)
@@ -1,5 +1,5 @@
 /* Creating and controlling threads.
-   Copyright (C) 2005-2008 Free Software Foundation, Inc.
+   Copyright (C) 2005-2011 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
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+/* Specification.  */
 #include "glthread/thread.h"
 
 #include <stdlib.h>
 
 #if USE_WIN32_THREADS
 
+#include <process.h>
+
 /* -------------------------- 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)
+/* 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)
 {
-  struct thread_extra *xarg = (struct thread_extra *)varg;
+  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);
+}
 
-  xarg->id = GetCurrentThreadId ();
-  /* Add xarg to the list of running thread_extra.  */
-  gl_lock_lock (running_lock);
-  if (!(xarg->id == GetCurrentThreadId ()))
+/* 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 inline 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 ();
-  xarg->next = running_threads;
-  running_threads = xarg;
-  gl_lock_unlock (running_lock);
+  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.  */
-  xarg->result = xarg->func (xarg->arg);
+  thread->result = thread->func (thread->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)
+  struct gl_thread_struct *thread =
+    (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct));
+  if (thread == NULL)
     return ENOMEM;
-  x->result = NULL; /* just to be deterministic */
-  x->func = func;
-  x->arg = arg;
+  thread->handle = NULL;
+  InitializeCriticalSection (&thread->handle_lock);
+  thread->result = NULL; /* just to be deterministic */
+  thread->func = func;
+  thread->arg = arg;
+
   {
-    DWORD thread_id;
+    unsigned int thread_id;
     HANDLE thread_handle;
 
-    gl_lock_lock (running_lock);
-    thread_handle = CreateThread (NULL, 100000, wrapper_func, x, 0, &thread_id);
+    thread_handle = (HANDLE)
+      _beginthreadex (NULL, 100000, wrapper_func, thread, 0, &thread_id);
+      /* calls CreateThread with the same arguments */
     if (thread_handle == NULL)
       {
-       gl_lock_unlock (running_lock);
-       return EAGAIN;
+        DeleteCriticalSection (&thread->handle_lock);
+        free (thread);
+        return EAGAIN;
       }
-    x->id = thread_id;
-    x->handle = thread_handle;
-    gl_lock_unlock (running_lock);
-    *threadp = thread_id;
+
+    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;
   }
 }
@@ -107,55 +185,21 @@ glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg)
 int
 glthread_join_func (gl_thread_t thread, void **retvalp)
 {
-  HANDLE thread_handle;
+  if (thread == NULL)
+    return EINVAL;
 
   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)
+  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);
+  if (retvalp != NULL)
+    *retvalp = thread->result;
 
-  CloseHandle (thread_handle);
+  DeleteCriticalSection (&thread->handle_lock);
+  CloseHandle (thread->handle);
+  free (thread);
 
   return 0;
 }
@@ -163,22 +207,10 @@ glthread_join_func (gl_thread_t thread, void **retvalp)
 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);
+  gl_thread_t thread = gl_thread_self ();
+  thread->result = retval;
+  _endthreadex (0); /* calls ExitThread (0) */
+  abort ();
 }
 
 #endif