Yoann Vandoorselaere wrote on 2008-09-15: > > - Make 'lock-tests' and 'tls-tests' depend on the thread and yield > > module, use YIELD_LIBS in place of the deprecated LIBSCHED variable, and > > update 'lock-tests.c' and 'tls-tests.c' to use newer glthread API.
Doing this broke test-lock on mingw. But after reimplementing the 'thread' module for win32, it works fine. So I'm first committing the corrected 'thread` module. 2008-09-30 Bruno Haible <[EMAIL PROTECTED]> Fix the Win32 implementation of the 'thread' module. * lib/glthread/thread.h [USE_WIN32_THREADS] (gl_thread_t): Change to a pointer type. (gl_thread_self): Invoke gl_thread_self_func. (gl_thread_self_func): New declaration. * lib/glthread/thread.c [USE_WIN32_THREADS] (self_key): New variable. (do_init_self_key, init_self_key): New functions. (struct gl_thread_struct): Renamed from 'struct thread_extra'. Remove some fields. (running_threads, running_lock): Remove variables. (get_current_thread_handle): New function. (gl_thread_self_func, wrapper_func, glthread_create_func, glthread_join_func, gl_thread_exit_func): Largely rewritten and simplified. *** lib/glthread/thread.h.orig 2008-10-01 02:22:43.000000000 +0200 --- lib/glthread/thread.h 2008-10-01 01:50:19.000000000 +0200 *************** *** 277,289 **** /* -------------------------- 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) \ --- 277,292 ---- /* -------------------------- gl_thread_t datatype -------------------------- */ ! /* The gl_thread_t is a pointer to a structure in memory. ! Why 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). ! Why not the thread id? I tried it. It did not work: Sometimes ids appeared ! that did not belong to running threads, and glthread_join failed with ESRCH. ! */ ! typedef struct gl_thread_struct *gl_thread_t; # define glthread_create(THREADP, FUNC, ARG) \ glthread_create_func (THREADP, FUNC, ARG) # define glthread_sigmask(HOW, SET, OSET) \ *************** *** 291,302 **** # 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 --- 294,306 ---- # define glthread_join(THREAD, RETVALP) \ glthread_join_func (THREAD, RETVALP) # define gl_thread_self() \ ! gl_thread_self_func () # 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 gl_thread_t gl_thread_self_func (void); extern int gl_thread_exit_func (void *retval); # ifdef __cplusplus *** lib/glthread/thread.c.orig 2008-10-01 02:22:43.000000000 +0200 --- lib/glthread/thread.c 2008-10-01 02:21:31.000000000 +0200 *************** *** 21,26 **** --- 21,27 ---- #include <config.h> + /* Specification. */ #include "glthread/thread.h" #include <stdlib.h> *************** *** 32,126 **** /* -------------------------- 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; ! CRITICAL_SECTION handle_lock; ! 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; ! 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 ())) ! 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->handle = NULL; ! InitializeCriticalSection (&x->handle_lock); ! x->result = NULL; /* just to be deterministic */ ! x->func = func; ! x->arg = arg; { DWORD thread_id; HANDLE thread_handle; ! thread_handle = CreateThread (NULL, 100000, wrapper_func, x, 0, &thread_id); if (thread_handle == NULL) { ! DeleteCriticalSection (&x->handle_lock); ! free (x); return EAGAIN; } ! EnterCriticalSection (&x->handle_lock); ! x->id = thread_id; ! 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; } } --- 33,180 ---- /* -------------------------- gl_thread_t datatype -------------------------- */ ! /* 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) ! { ! 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); ! } ! ! /* 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 (); ! 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 DWORD 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. */ ! thread->result = thread->func (thread->arg); return 0; } int glthread_create_func (gl_thread_t *threadp, void * (*func) (void *), void *arg) { ! struct gl_thread_struct *thread = ! (struct gl_thread_struct *) malloc (sizeof (struct gl_thread_struct)); ! if (thread == NULL) return ENOMEM; ! thread->handle = NULL; ! InitializeCriticalSection (&thread->handle_lock); ! thread->result = NULL; /* just to be deterministic */ ! thread->func = func; ! thread->arg = arg; ! { DWORD thread_id; HANDLE thread_handle; ! thread_handle = ! CreateThread (NULL, 100000, wrapper_func, thread, 0, &thread_id); if (thread_handle == NULL) { ! DeleteCriticalSection (&thread->handle_lock); ! free (thread); return EAGAIN; } ! ! 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; } } *************** *** 128,181 **** 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 or ! wrapper_func (whichever was executed first) has filled in x->handle. */ ! 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 * volatile *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; ! DeleteCriticalSection (&x->handle_lock); ! free (x); ! break; ! } ! } ! gl_lock_unlock (running_lock); ! CloseHandle (thread_handle); return 0; } --- 182,202 ---- int glthread_join_func (gl_thread_t thread, void **retvalp) { ! if (thread == NULL) ! return EINVAL; if (thread == gl_thread_self ()) return EDEADLK; ! if (WaitForSingleObject (thread->handle, INFINITE) == WAIT_FAILED) return EINVAL; ! if (retvalp != NULL) ! *retvalp = thread->result; ! DeleteCriticalSection (&thread->handle_lock); ! CloseHandle (thread->handle); ! free (thread); return 0; } *************** *** 183,203 **** 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); } --- 204,211 ---- int gl_thread_exit_func (void *retval) { ! gl_thread_t thread = gl_thread_self (); ! thread->result = retval; ExitThread (0); }