From: Bruno Haible Date: Mon, 18 Aug 2008 00:23:32 +0000 (+0200) Subject: Implement thread control primitives for Win32. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ecbcf1d92dc5993c972c0b5f002ef0b04abf97b8;p=pspp Implement thread control primitives for Win32. --- diff --git a/ChangeLog b/ChangeLog index 7cf29c2c79..f1c6180c4e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-08-17 Bruno Haible + + * 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 New module 'yield'. diff --git a/lib/glthread/thread.c b/lib/glthread/thread.c new file mode 100644 index 0000000000..9894b8d3d1 --- /dev/null +++ b/lib/glthread/thread.c @@ -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 , 2005. + Based on GCC's gthr-posix.h, gthr-posix95.h, gthr-solaris.h, + gthr-win32.h. */ + +#include + +#include "glthread/thread.h" + +#include +#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 + +/* ========================================================================= */ diff --git a/lib/glthread/thread.h b/lib/glthread/thread.h index 5c98cddd28..d88908e015 100644 --- a/lib/glthread/thread.h +++ b/lib/glthread/thread.h @@ -267,6 +267,46 @@ typedef thread_t gl_thread_t; /* ========================================================================= */ +#if USE_WIN32_THREADS + +# include + +# 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. */ diff --git a/modules/thread b/modules/thread index d9586b58eb..a7d338df4c 100644 --- a/modules/thread +++ b/modules/thread @@ -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"