changeset 10335:9a346ffa242e

Avoid forcing a context switch right after thread creation.
author Bruno Haible <bruno@clisp.org>
date Mon, 18 Aug 2008 11:39:40 +0200
parents 392ab85684a6
children 8a9ef2dbc161
files ChangeLog lib/glthread/thread.c
diffstat 2 files changed, 34 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-08-18  Bruno Haible  <bruno@clisp.org>
+
+	* lib/glthread/thread.c: Avoid forcing a context switch right after
+	thread creation.
+
 2008-08-17  Bruno Haible  <bruno@clisp.org>
 
 	* lib/glthread/thread.c: New file, based on code from tests/test-lock.c.
--- a/lib/glthread/thread.c
+++ b/lib/glthread/thread.c
@@ -39,6 +39,7 @@
     /* Fields for managing the association between thread id and handle.  */
     DWORD volatile id;
     HANDLE volatile handle;
+    CRITICAL_SECTION handle_lock;
     struct thread_extra * volatile next;
     /* Fields for managing the exit value.  */
     void * volatile result;
@@ -60,7 +61,20 @@
 {
   struct thread_extra *xarg = (struct thread_extra *)varg;
 
+  EnterCriticalSection (&xarg->handle_lock);
   xarg->id = GetCurrentThreadId ();
+  /* Create a new handle for the thread only if the parent thread did not yet
+     fill in the handle.  */
+  if (xarg->handle == NULL)
+    {
+      HANDLE this_thread;
+      if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
+			    GetCurrentProcess (), &this_thread,
+			    0, FALSE, DUPLICATE_SAME_ACCESS))
+	abort ();
+      xarg->handle = this_thread;
+    }
+  LeaveCriticalSection (&xarg->handle_lock);
   /* Add xarg to the list of running thread_extra.  */
   gl_lock_lock (running_lock);
   if (!(xarg->id == GetCurrentThreadId ()))
@@ -82,6 +96,8 @@
     (struct thread_extra *) malloc (sizeof (struct thread_extra));
   if (x == NULL)
     return ENOMEM;
+  x->handle = NULL;
+  InitializeCriticalSection (&x->handle_lock);
   x->result = NULL; /* just to be deterministic */
   x->func = func;
   x->arg = arg;
@@ -89,16 +105,21 @@
     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);
+	DeleteCriticalSection (&x->handle_lock);
+	free (x);
 	return EAGAIN;
       }
+    EnterCriticalSection (&x->handle_lock);
     x->id = thread_id;
-    x->handle = thread_handle;
-    gl_lock_unlock (running_lock);
+    if (x->handle == NULL)
+      x->handle = thread_handle;
+    else
+      /* x->handle was already set by the thread itself.  */
+      CloseHandle (thread_handle);
+    LeaveCriticalSection (&x->handle_lock);
     *threadp = thread_id;
     return 0;
   }
@@ -114,10 +135,8 @@
 
   /* 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 itself.  So at this point, either glthread_create_func or
+     wrapper_func (whichever was executed first) has filled in x->handle.  */
   thread_handle = NULL;
   gl_lock_lock (running_lock);
   {
@@ -139,7 +158,7 @@
   /* Remove the 'struct thread_extra' from running_threads.  */
   gl_lock_lock (running_lock);
   {
-    struct thread_extra **xp;
+    struct thread_extra * volatile *xp;
     for (xp = &running_threads; *xp != NULL; xp = &(*xp)->next)
       if ((*xp)->id == thread)
 	{
@@ -149,6 +168,7 @@
 	  if (x->handle != thread_handle)
 	    abort ();
 	  *xp = x->next;
+	  DeleteCriticalSection (&x->handle_lock);
 	  free (x);
 	  break;
 	}