bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: checkin of glthread/glcond modules


From: Bruno Haible
Subject: Re: checkin of glthread/glcond modules
Date: Mon, 18 Aug 2008 02:23:10 +0200
User-agent: KMail/1.5.4

Here is the Win32 implementation of the thread module.
Please review it; it's quite complicated. The complexity comes from
  1. the need to have a 'void *' as thread exit value, where Win32 only
     manages a 32-bit value,
  2. weird thread APIs: In Win32, every thread has an id and multiple handles.
     But It's hard to get a handle for the current thread. And in order to
     wait for a thread, you need one of its handles...


2008-08-17  Bruno Haible  <address@hidden>

        * 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.

*** lib/glthread/thread.c.orig  2003-09-23 19:59:22.000000000 +0200
--- lib/glthread/thread.c       2008-08-18 02:17:39.000000000 +0200
***************
*** 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 <address@hidden>, 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
+ 
+ /* ========================================================================= 
*/
*** lib/glthread/thread.h.orig  2008-08-18 02:17:53.000000000 +0200
--- lib/glthread/thread.h       2008-08-18 02:16:32.000000000 +0200
***************
*** 267,272 ****
--- 267,312 ----
  
  /* ========================================================================= 
*/
  
+ #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.  */
*** modules/thread.orig 2008-08-18 02:17:53.000000000 +0200
--- modules/thread      2008-08-18 00:55:28.000000000 +0200
***************
*** 3,18 ****
  
  Files:
  lib/glthread/thread.h
  m4/thread.m4
  
  Depends-on:
  threadlib
  
  configure.ac:
  gl_THREAD
  
  Makefile.am:
! lib_SOURCES += glthread/thread.h 
  
  Include:
  "glthread/thread.h"
--- 3,20 ----
  
  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 glthread/thread.c
  
  Include:
  "glthread/thread.h"





reply via email to

[Prev in Thread] Current Thread [Next in Thread]