From 36f0ed4155fdab7b12c5c5ddf4252170fac0a77e Mon Sep 17 00:00:00 2001 From: rpj Date: Sun, 3 Jan 1999 18:47:50 +0000 Subject: Merge John Bossom's code into the main trunk. See ChangeLog for details. This will be tagged as snapshot-1999-01-04-1305 --- ChangeLog | 150 +++++++- MAINTAINERS | 2 + attr.c | 350 +++++++++++++++++- buildlib.bat | 3 + cancel.c | 246 ++++++++++--- cleanup.c | 306 ++++++---------- condvar.c | 762 ++++++++++++++++++++++++++++++++------- create.c | 231 ++++++------ dll.c | 119 ++++--- exit.c | 87 ++--- fork.c | 6 +- global.c | 72 +--- implement.h | 292 +++++++-------- misc.c | 211 ++++++++++- private.c | 435 ++++++++++++++++++----- pthread.def | 4 +- pthread.h | 1041 ++++++++++++++++++++++++++++++++++++++++++------------ sched.c | 20 +- semaphore.c | 254 +++++++++++++ semaphore.h | 52 +++ sync.c | 243 +++++-------- tests/ChangeLog | 6 + tests/Template.c | 68 ++++ tests/tsd1.c | 116 ++++-- tsd.c | 436 ++++++++++++++--------- 25 files changed, 4006 insertions(+), 1506 deletions(-) create mode 100644 semaphore.c create mode 100644 semaphore.h create mode 100644 tests/Template.c diff --git a/ChangeLog b/ChangeLog index 04b46ac..ae2d20d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,18 +2,166 @@ * README: Update info about subscribing to the mailing list. +Mon Jan 4 11:23:40 1999 Ross Johnson + + * all: No code changes, just cleanup. + - remove #if 0 /* Pre Bossom */ enclosed code. + - Remove some redundant #includes. + * pthread.h: Update implemented/unimplemented routines list. + * Tag the bossom merge branch getting ready to merge back to main + trunk. + +Tue Dec 29 13:11:16 1998 Ross Johnson + + * implement.h: Move the following struct definitions to pthread.h: + pthread_t_, pthread_attr_t_, pthread_mutex_t_, pthread_mutex_t_, + pthread_mutexattr_t_, pthread_key_t_, pthread_cond_t_, + pthread_condattr_t_, pthread_once_t_. + + * pthread.h: Add "_" prefix to pthread_push_cleanup and + pthread_pop_cleanup internal routines, and associated struct and + typedefs. + + * buildlib.bat: Add compile command for semaphore.c + + * pthread.def: Comment out pthread_atfork routine name. + Now unimplemented. + + * tsd.c (pthread_setspecific): Rename tkAssocCreate to + _pthread_tkAssocCreate. + (pthread_key_delete): Rename tkAssocDestroy to + _pthread_tkAssocDestroy. + + * sync.c (pthread_join): Rename threadDestroy to _pthread_threadDestroy + + * sched.c (is_attr): attr is now **attr (was *attr), so add extra + NULL pointer test. + (pthread_attr_setschedparam): Increase redirection for attr which is + now a **. + (pthread_attr_getschedparam): Ditto. + (pthread_setschedparam): Change thread validation and rename "thread" + Win32 thread Handle element name to match John Bossom's version. + (pthread_getschedparam): Ditto. + + * private.c (_pthread_threadDestroy): Rename call to + callUserDestroyRoutines() as _pthread_callUserDestroyRoutines() + + * misc.c: Add #include "implement.h". + + * dll.c: Remove defined(KLUDGE) wrapped code. + + * fork.c: Remove redefinition of ENOMEM. + Remove pthread_atfork() and fork() with #if 0/#endif. + + * create.c (pthread_create): Rename threadStart and threadDestroy calls + to _pthread_threadStart and _pthread_threadDestroy. + + * implement.h: Rename "detachedstate" to "detachstate". + + * attr.c: Rename "detachedstate" to "detachstate". + +Mon Dec 28 09:54:39 1998 Ross Johnson + + * semaphore.c: Initial version. From John Bossom's implementation. + * semaphore.h: Initial version. From John Bossom's implementation. + +Mon Dec 28 09:54:39 1998 Ross Johnson + + * pthread.h (pthread_attr_t_): Change to *pthread_attr_t. + + * attr.c (pthread_attr_setstacksize): Merge with John Bossom's version. + (pthread_attr_getstacksize): Merge with John Bossom's version. + (pthread_attr_setstackaddr): Merge with John Bossom's version. + (pthread_attr_getstackaddr): Merge with John Bossom's version. + (pthread_attr_init): Merge with John Bossom's version. + (pthread_attr_destroy): Merge with John Bossom's version. + (pthread_attr_getdetachstate): Merge with John Bossom's version. + (pthread_attr_setdetachstate): Merge with John Bossom's version. + (is_attr): attr is now **attr (was *attr), so add extra NULL pointer + test. + + * implement.h (pthread_attr_t_): Add and rename elements in JEB's + version to correspond to original, so that it can be used with + original attr routines. + + * pthread.h: Add #endif at end which was truncated in merging. + +Sun Dec 20 14:51:58 1998 Ross Johnson + + * misc.c (pthreadCancelableWait): New function by John Bossom. Non-standard + but provides a hook that can be used to implement cancellation points in + applications that use this library. + + * pthread.h (pthread_cleanup_pop): C++ (non-WIN32) version uses + try/catch to emulate John Bossom's WIN32 __try/__finally behaviour. + In the WIN32 version __finally block, add a test for AbnormalTermination otherwise + cleanup is only run if the cleanup_pop execute arg is non-zero. Cancellation + should cause the cleanup to run irrespective of the execute arg. + + * condvar.c (pthread_condattr_init): Replaced by John Bossom's version. + (pthread_condattr_destroy): Replaced by John Bossom's version. + (pthread_condattr_getpshared): Replaced by John Bossom's version. + (pthread_condattr_setpshared): Replaced by John Bossom's version. + (pthread_cond_init): Replaced by John Bossom's version. + Fix comment (refered to mutex rather than condition variable). + (pthread_cond_destroy): Replaced by John Bossom's version. + (pthread_cond_wait): Replaced by John Bossom's version. + (pthread_cond_timedwait): Replaced by John Bossom's version. + (pthread_cond_signal): Replaced by John Bossom's version. + (pthread_cond_broadcast): Replaced by John Bossom's version. + +Thu Dec 17 19:10:46 1998 Ross Johnson + + * tsd.c (pthread_key_create): Replaced by John Bossom's version. + (pthread_key_delete): Replaced by John Bossom's version. + (pthread_setspecific): Replaced by John Bossom's version. + (pthread_getspecific): Replaced by John Bossom's version. + Mon Dec 7 09:44:40 1998 Ross Johnson + * cancel.c (pthread_setcancelstate): Replaced by John Bossom's version. + (pthread_setcanceltype): Replaced by John Bossom's version. + (pthread_testcancel): Replaced by John Bossom's version. + (pthread_cancel): Replaced by John Bossom's version. + + * exit.c (pthread_exit): Replaced by John Bossom's version. + + * misc.c (pthread_self): Replaced by John Bossom's version. + (pthread_equal): Replaced by John Bossom's version. + + * sync.c (pthread_detach): Replaced by John Bossom's version. + (pthread_join): Replaced by John Bossom's version. + + * create.c (pthread_create): Replaced by John Bossom's version. + + * private.c (_pthread_processInitialize): New by John Bossom. + (_pthread_processTerminate): Non-public function by John Bossom. + (_pthread_threadStart): Non-public function by John Bossom. + (_pthread_threadDestroy): Non-public function by John Bossom. + (_pthread_cleanupStack): Non-public function by John Bossom. + (_pthread_tkAssocCreate): Non-public function by John Bossom. + (_pthread_tkAssocDestroy): Non-public function by John Bossom. + (_pthread_callUserDestroyRoutines): Non-public function by John Bossom. + + * implement.h: Added John Bossom's non-API structures and + declarations. + * dll.c (PthreadsEntryPoint): Cast return value of GetProcAddress to resolve compile warning from MSVC. + * dll.c (DLLmain): Replaced by John Bossom's version. + * dll.c (PthreadsEntryPoint): + Re-applied Anders Norlander's patch:- + Initialize _pthread_try_enter_critical_section at startup + and release kernel32 handle when DLL is being unloaded. + Sun Dec 6 21:54:35 1998 Ross Johnson * buildlib.bat: Fix args to CL when building the .DLL * cleanup.c (_pthread_destructor_run_all): Fix TSD key management. This is a tidy-up before TSD and Thread management is completely - replaced by John Bossom's much more elegant code. + replaced by John Bossom's code. * tsd.c (pthread_key_create): Fix TSD key management. diff --git a/MAINTAINERS b/MAINTAINERS index 3baf225..a09e2c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8,3 +8,5 @@ Ross Johnson rpj@ise.canberra.edu.au Active contributors Robert Colquhoun rjc@trump.net.au +John E. Bossom John.Bossom@cognos.com +Anders Norlander anorland@hem2.passagen.se diff --git a/attr.c b/attr.c index 418c3e8..3ec1d12 100644 --- a/attr.c +++ b/attr.c @@ -16,15 +16,57 @@ is_attr(const pthread_attr_t *attr) { /* Return 0 if the attr object is valid, non-zero otherwise. */ - return (attr == NULL || attr->valid != _PTHREAD_ATTR_VALID); + return (attr == NULL || + *attr == NULL || + (*attr)->valid != _PTHREAD_ATTR_VALID); } -#ifdef _POSIX_THREAD_ATTR_STACKSIZE int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function specifies the size of the stack on + * which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * stack size, in bytes. + * + * + * DESCRIPTION + * This function specifies the size of the stack on + * which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKSIZE + * + * 2) Find the default first (using + * pthread_attr_getstacksize), then increase + * by multiplying. + * + * 3) Only use if thread needs more than the + * default. + * + * RESULTS + * 0 successfully set stack size, + * EINVAL 'attr' is invalid or stacksize too + * small or too big. + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ { +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + /* Verify that the stack size is within range. */ if (stacksize < PTHREAD_STACK_MIN) { @@ -37,89 +79,295 @@ pthread_attr_setstacksize(pthread_attr_t *attr, } /* Everything is okay. */ - attr->stacksize = stacksize; + (*attr)->stacksize = stacksize; return 0; + +#else + + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKSIZE */ + } int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines the size of the stack on + * which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * pointer to size_t into which is returned the + * stack size, in bytes. + * + * + * DESCRIPTION + * This function determines the size of the stack on + * which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKSIZE + * + * 2) Use on newly created attributes object to + * find the default stack size. + * + * RESULTS + * 0 successfully retrieved stack size, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ { +#ifdef _POSIX_THREAD_ATTR_STACKSIZE + if (is_attr(attr) != 0) { return EINVAL; } /* Everything is okay. */ - *stacksize = attr->stacksize; + *stacksize = (*attr)->stacksize; return 0; -} + +#else + + return ENOSYS; #endif /* _POSIX_THREAD_ATTR_STACKSIZE */ -#ifdef _POSIX_THREAD_ATTR_STACKADDR +} + int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Threads created with 'attr' will run on the stack + * starting at 'stackaddr'. + * Stack must be at least PTHREAD_STACK_MIN bytes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stacksize + * stack size, in bytes. + * + * + * DESCRIPTION + * Threads created with 'attr' will run on the stack + * starting at 'stackaddr'. + * Stack must be at least PTHREAD_STACK_MIN bytes. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKADDR + * + * 2) Create only one thread for each stack + * address.. + * + * 3) Ensure that stackaddr is aligned. + * + * RESULTS + * 0 successfully set stack address, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ { +#if defined( _POSIX_THREAD_ATTR_STACKADDR ) + if (is_attr(attr) != 0) { return EINVAL; } + + (*attr)->stackaddr = stackaddr; + return 0; + +#else + return ENOSYS; + +#endif /* _POSIX_THREAD_ATTR_STACKADDR */ } int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines the address of the stack + * on which threads created with 'attr' will run. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * stackaddr + * pointer into which is returned the stack address. + * + * + * DESCRIPTION + * This function determines the address of the stack + * on which threads created with 'attr' will run. + * + * NOTES: + * 1) Function supported only if this macro is + * defined: + * + * _POSIX_THREAD_ATTR_STACKADDR + * + * 2) Create only one thread for each stack + * address.. + * + * RESULTS + * 0 successfully retreived stack address, + * EINVAL 'attr' is invalid + * ENOSYS function not supported + * + * ------------------------------------------------------ + */ { +#if defined( _POSIX_THREAD_ATTR_STACKADDR ) + if (is_attr(attr) != 0) { return EINVAL; } + + *stackaddr = (*attr)->stackaddr; + return 0; + +#else + return ENOSYS; -} #endif /* _POSIX_THREAD_ATTR_STACKADDR */ +} int pthread_attr_init(pthread_attr_t *attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a thread attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * + * DESCRIPTION + * Initializes a thread attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define thread attributes + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ { + pthread_attr_t attr_result; + int result = 0; + if (attr == NULL) { /* This is disallowed. */ return EINVAL; } + attr_result = malloc (sizeof (*attr_result)); + + if (attr_result == NULL) + { + return ENOMEM; + } + #ifdef _POSIX_THREAD_ATTR_STACKSIZE - attr->stacksize = PTHREAD_STACK_MIN; + attr_result->stacksize = PTHREAD_STACK_MIN; #endif - attr->detachedstate = PTHREAD_CREATE_JOINABLE; +#ifdef _POSIX_THREAD_ATTR_STACKADDR + /* FIXME: Set this to something sensible when we support it. */ + attr_result->stackaddr = NULL; +#endif + + attr_result->detachstate = PTHREAD_CREATE_JOINABLE; + #if HAVE_SIGSET_T - memset(&(attr->sigmask), 0, sizeof(sigset_t)); + memset(&(attr_result->sigmask), 0, sizeof(sigset_t)); #endif /* HAVE_SIGSET_T */ /* Priority uses Win32 priority values. */ - attr->priority = THREAD_PRIORITY_NORMAL; + attr_result->priority = THREAD_PRIORITY_NORMAL; + + attr_result->valid = _PTHREAD_ATTR_VALID; - attr->valid = _PTHREAD_ATTR_VALID; + *attr = attr_result; return 0; } int pthread_attr_destroy(pthread_attr_t *attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a thread attributes object. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * + * DESCRIPTION + * Destroys a thread attributes object. + * + * NOTES: + * 1) Does not affect threads created with 'attr'. + * + * RESULTS + * 0 successfully destroyed attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ { if (is_attr(attr) != 0) { return EINVAL; } - /* Set the attribute object to a specific invalid value. */ - attr->valid = 0; + /* + * Set the attribute object to a specific invalid value. + */ + (*attr)->valid = 0; + free (*attr); + *attr = NULL; return 0; } @@ -127,19 +375,89 @@ pthread_attr_destroy(pthread_attr_t *attr) int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function determines whether threads created with + * 'attr' will run detached. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * detachstate + * pointer to an integer into which is returned one + * of: + * + * PTHREAD_CREATE_JOINABLE + * Thread ID is valid, must be joined + * + * PTHREAD_CREATE_DETACHED + * Thread ID is invalid, cannot be joined, + * canceled, or modified + * + * + * DESCRIPTION + * This function determines whether threads created with + * 'attr' will run detached. + * + * NOTES: + * 1) You cannot join or cancel detached threads. + * + * RESULTS + * 0 successfully retrieved detach state, + * EINVAL 'attr' is invalid + * + * ------------------------------------------------------ + */ { if (is_attr(attr) != 0 || detachstate == NULL) { + *detachstate = PTHREAD_CREATE_DETACHED; return EINVAL; } - *detachstate = attr->detachedstate; + *detachstate = (*attr)->detachstate; return 0; } int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function specifies whether threads created with + * 'attr' will run detached. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_attr_t + * + * detachstate + * an integer containing one of: + * + * PTHREAD_CREATE_JOINABLE + * Thread ID is valid, must be joined + * + * PTHREAD_CREATE_DETACHED + * Thread ID is invalid, cannot be joined, + * canceled, or modified + * + * + * DESCRIPTION + * This function specifies whether threads created with + * 'attr' will run detached. + * + * NOTES: + * 1) You cannot join or cancel detached threads. + * + * RESULTS + * 0 successfully set detach state, + * EINVAL 'attr' or 'detachstate' is invalid + * + * ------------------------------------------------------ + */ { if (is_attr(attr) != 0) { @@ -152,6 +470,6 @@ pthread_attr_setdetachstate(pthread_attr_t *attr, return EINVAL; } - attr->detachedstate = detachstate; + (*attr)->detachstate = detachstate; return 0; } diff --git a/buildlib.bat b/buildlib.bat index e8a674e..5e2e1c8 100644 --- a/buildlib.bat +++ b/buildlib.bat @@ -16,5 +16,8 @@ cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c sched. cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c signal.c cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c sync.c cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c tsd.c +cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c semaphore.c cl /LD /Zi *.obj /Fepthread.dll /link /nodefaultlib:libcmt /implib:pthread.lib msvcrt.lib /def:pthread.def + + diff --git a/cancel.c b/cancel.c index 9dd99c6..93d6e07 100644 --- a/cancel.c +++ b/cancel.c @@ -5,80 +5,238 @@ * POSIX thread functions related to thread cancellation. */ -#include - #include "pthread.h" #include "implement.h" +/* + * Code contributed by John E. Bossom . + */ + int -pthread_setcancelstate(int state, - int *oldstate) +pthread_setcancelstate (int state, int *oldstate) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function atomically sets the calling thread's + * cancelability state to 'state' and returns the previous + * cancelability state at the location referenced by + * 'oldstate' + * + * PARAMETERS + * type, + * oldtype + * PTHREAD_CANCEL_ENABLE + * cancellation is enabled, + * + * PTHREAD_CANCEL_DISABLE + * cancellation is disabled + * + * + * DESCRIPTION + * This function atomically sets the calling thread's + * cancelability state to 'state' and returns the previous + * cancelability state at the location referenced by + * 'oldstate' + * + * NOTES: + * 1) Use to disable cancellation around 'atomic' code that + * includes cancellation points + * + * RESULTS + * 0 successfully set cancelability type, + * EINVAL 'state' is invalid + * + * ------------------------------------------------------ + */ { - pthread_t us = pthread_self(); + pthread_t self; + int result; - /* Validate the new cancellation state. */ - if (state != PTHREAD_CANCEL_ENABLE - || state != PTHREAD_CANCEL_DISABLE) + if (((self = pthread_self ()) != NULL) && + (state == PTHREAD_CANCEL_ENABLE || + state == PTHREAD_CANCEL_DISABLE)) { - return EINVAL; - } - if (oldstate != NULL) + *oldstate = self->cancelState; + self->cancelState = state; + result = 0; + + } + else { - *oldstate = us->cancelstate; + result = EINVAL; } - us->cancelstate = state; - return 0; -} + return (result); + +} /* pthread_setcancelstate */ + int -pthread_setcanceltype(int type, int *oldtype) +pthread_setcanceltype (int type, int *oldtype) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function atomically sets the calling thread's + * cancelability type to 'type' and returns the previous + * cancelability type at the location referenced by + * 'oldtype' + * + * PARAMETERS + * type, + * oldtype + * PTHREAD_CANCEL_DEFERRED + * only deferred cancelation is allowed, + * + * PTHRAD_CANCEL_ASYNCHRONOUS + * Asynchronous cancellation is allowed + * + * + * DESCRIPTION + * This function atomically sets the calling thread's + * cancelability type to 'type' and returns the previous + * cancelability type at the location referenced by + * 'oldtype' + * + * NOTES: + * 1) Use with caution; most code is not safe for use + * with asynchronous cancelability. + * + * RESULTS + * 0 successfully set cancelability type, + * EINVAL 'type' is invalid + * + * ------------------------------------------------------ + */ { - pthread_t us = pthread_self(); + pthread_t self; + int result; - /* Validate the new cancellation type. */ - if (type == PTHREAD_CANCEL_ASYNCHRONOUS || - type != PTHREAD_CANCEL_DEFERRED) + if (((self = pthread_self ()) != NULL) && + (type == PTHREAD_CANCEL_DEFERRED || + type == PTHREAD_CANCEL_ASYNCHRONOUS)) { - return EINVAL; - } - if (oldtype != NULL) + *oldtype = self->cancelType; + self->cancelType = type; + result = 0; + + } + else { - *oldtype = us->canceltype; + result = EINVAL; } - us->canceltype = type; - return 0; -} + return (result); -int -pthread_cancel(pthread_t thread) +} /* pthread_setcanceltype */ + +void +pthread_testcancel (void) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a deferred cancellation point + * in the calling thread. The call has no effect if the + * current cancelability state is + * PTHREAD_CANCEL_DISABLE + * + * PARAMETERS + * N/A + * + * + * DESCRIPTION + * This function creates a deferred cancellation point + * in the calling thread. The call has no effect if the + * current cancelability state is + * PTHREAD_CANCEL_DISABLE + * + * NOTES: + * 1) Cancellation is asynchronous. Use pthread_join + * to wait for termination of thread if necessary + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ { - if (_PTHREAD_VALID(thread) - && thread->ptstatus != _PTHREAD_REUSE) + pthread_t self; + + if ((self = pthread_getspecific (_pthread_selfThreadKey)) != NULL) { - thread->cancel_pending = TRUE; - return 0; + + if (self->cancelState == PTHREAD_CANCEL_ENABLE) + { + + if (WaitForSingleObject (self->cancelEvent, 0) == + WAIT_OBJECT_0) + { + /* + * Canceling! + */ + DWORD exceptionInformation[3]; + + exceptionInformation[0] = (DWORD) (0); + exceptionInformation[1] = (DWORD) (0); + + RaiseException ( + EXCEPTION_PTHREAD_SERVICES, + 0, + 3, + exceptionInformation); + } + } } - return ESRCH; -} +} /* pthread_testcancel */ -void -pthread_testcancel(void) +int +pthread_cancel (pthread_t thread) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function requests cancellation of 'thread'. + * + * PARAMETERS + * thread + * reference to an instance of pthread_t + * + * + * DESCRIPTION + * This function requests cancellation of 'thread'. + * NOTE: cancellation is asynchronous; use pthread_join to + * wait for termination of 'thread' if necessary. + * + * RESULTS + * 0 successfully created semaphore, + * ESRCH no thread found corresponding to 'thread', + * + * ------------------------------------------------------ + */ { - pthread_t thread = pthread_self(); + int result; - if (thread->cancelstate == PTHREAD_CANCEL_DISABLE) + if (thread != NULL) { - return; - } - if (thread->cancel_pending == TRUE) + if (!SetEvent (thread->cancelEvent)) + { + result = ESRCH; + } + else + { + result = 0; + } + + } + else { - pthread_exit(PTHREAD_CANCELED); + result = ESRCH; } - /* Never reached. */ + + return (result); } + +/* */ + diff --git a/cleanup.c b/cleanup.c index 0a5ae3f..12ce282 100644 --- a/cleanup.c +++ b/cleanup.c @@ -6,211 +6,139 @@ * threads. */ -#include - #include + #include "pthread.h" #include "implement.h" -int -_pthread_handler_push(int stack, - int poporder, - void (*routine)(void *), - void *arg) -{ - /* Place the new handler into the list so that handlers are - popped off in the order given by poporder. */ - _pthread_handler_node_t * new_handler; - _pthread_handler_node_t * next; - _pthread_handler_node_t ** stacktop; - - stacktop = _PTHREAD_STACK(stack); +/* + * Code contributed by John E. Bossom . + */ - new_handler = - (_pthread_handler_node_t *) malloc(sizeof(_pthread_handler_node_t)); +_pthread_cleanup_t * +_pthread_pop_cleanup (int execute) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function pops the most recently pushed cleanup + * handler. If execute is nonzero, then the cleanup handler + * is executed if non-null. + * + * PARAMETERS + * execute + * if nonzero, execute the cleanup handler + * + * + * DESCRIPTION + * This function pops the most recently pushed cleanup + * handler. If execute is nonzero, then the cleanup handler + * is executed if non-null. + * NOTE: specify 'execute' as nonzero to avoid duplication + * of common cleanup code. + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + _pthread_cleanup_t *cleanup; + + cleanup = pthread_getspecific (_pthread_cleanupKey); - if (new_handler == NULL) + if (cleanup != NULL) { - return 0; /* NOMEM */ + if (execute && (cleanup->routine != NULL)) + { + +#ifdef _WIN32 + + __try + { + /* + * Run the caller's cleanup routine. + */ + (*cleanup->routine) (cleanup->arg); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* + * A system unexpected exception had occurred + * running the user's cleanup routine. + * We get control back within this block. + */ + } + } + +#else + + /* + * Run the caller's cleanup routine. + */ + (*cleanup->routine) (cleanup->arg); + +#endif /* _WIN32 */ + + pthread_setspecific (_pthread_cleanupKey, cleanup->prev); } - new_handler->routine = routine; - new_handler->arg = arg; + return (cleanup); - if (poporder == _PTHREAD_HANDLER_POP_LIFO) - { - /* Add the new node to the start of the list. */ - new_handler->next = *stacktop; - *stacktop = new_handler; - } - else - { - /* Add the new node to the end of the list. */ - new_handler->next = NULL; - - if (*stacktop == NULL) - { - *stacktop = new_handler; - } - else - { - next = *stacktop; - - while (next->next != NULL) - { - next = next->next; - } - - next->next = new_handler; - } - } - return 0; -} +} /* _pthread_pop_cleanup */ -void -_pthread_handler_pop(int stack, int execute) -{ - _pthread_handler_node_t ** stacktop; - _pthread_handler_node_t * next; - void (* func)(void *); - void * arg; - - stacktop = _PTHREAD_STACK(stack); - - if (*stacktop != NULL) - { - func = (*stacktop)->routine; - arg = (*stacktop)->arg; - next = (*stacktop)->next; - - free(*stacktop); - *stacktop = next; - - if (execute != 0 && func != NULL) - { - (void) func(arg); - } - } -} void -_pthread_handler_pop_all(int stack, int execute) +_pthread_push_cleanup (_pthread_cleanup_t * cleanup, + void (*routine) (void *), + void *arg) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function pushes a new cleanup handler onto the thread's stack + * of cleanup handlers. Each cleanup handler pushed onto the stack is + * popped and invoked with the argument 'arg' when + * a) the thread exits by calling 'pthread_exit', + * b) when the thread acts on a cancellation request, + * c) or when the thrad calls pthread_cleanup_pop with a nonzero + * 'execute' argument + * + * PARAMETERS + * cleanup + * a pointer to an instance of pthread_cleanup_t, + * + * routine + * pointer to a cleanup handler, + * + * arg + * parameter to be passed to the cleanup handler + * + * + * DESCRIPTION + * This function pushes a new cleanup handler onto the thread's stack + * of cleanup handlers. Each cleanup handler pushed onto the stack is + * popped and invoked with the argument 'arg' when + * a) the thread exits by calling 'pthread_exit', + * b) when the thread acts on a cancellation request, + * c) or when the thrad calls pthread_cleanup_pop with a nonzero + * 'execute' argument + * NOTE: pthread_push_cleanup, pthread_pop_cleanup must be paired + * in the same lexical scope. + * + * RESULTS + * pthread_cleanup_t * + * pointer to the previous cleanup + * + * ------------------------------------------------------ + */ { - /* Pop and possibly run all handlers on the given stack. */ - _pthread_handler_node_t ** stacktop; - _pthread_handler_node_t * next; - void (* func)(void *); - void * arg; + cleanup->routine = routine; + cleanup->arg = arg; + cleanup->prev = pthread_getspecific (_pthread_cleanupKey); - stacktop = _PTHREAD_STACK(stack); + pthread_setspecific (_pthread_cleanupKey, (void *) cleanup); - while (*stacktop != NULL) - { - func = (*stacktop)->routine; - arg = (*stacktop)->arg; - next = (*stacktop)->next; +} /* _pthread_push_cleanup */ - free(*stacktop); - *stacktop = next; +/* */ - if (execute != 0 && func != NULL) - { - (void) func(arg); - } - } -} -/* Run destructors for all non-NULL key values for the calling thread. - */ -void -_pthread_destructor_run_all() -{ - _pthread_tsd_key_t * key; - int count; - int dirty; - - /* This threads private keys */ - key = _pthread_tsd_key_table; - - /* Stop destructor execution at a finite time. POSIX allows us - to ignore this if we like, even at the risk of an infinite loop. - - FIXME: We don't know when to stop yet. - */ - for (count = 0; count < PTHREAD_DESTRUCTOR_ITERATIONS; count++) - { - int k; - void * arg; - - dirty = 0; - - /* Loop through all keys. */ - for (k = 0; k < _POSIX_THREAD_KEYS_MAX; k++) - { - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); - - switch (key->status) - { - case _PTHREAD_TSD_KEY_INUSE: - arg = pthread_getspecific((pthread_key_t) k); - - if (arg != NULL && key->destructor != NULL) - { - /* The destructor must be called with the mutex off. */ - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - - /* FIXME: Is the destructor supposed to set the key value - to NULL? How is this done when arg is the key value, not - a pointer to it? For now we assume that the destructor - always succeeds. - */ - (void) (key->destructor)(arg); - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); - - pthread_setspecific((pthread_key_t) k, NULL); -#if 0 - /* Only needed if we don't assume the destructor - always succeeds. - */ - dirty = 1; -#endif - } - break; - - case _PTHREAD_TSD_KEY_DELETED: - key->status = _PTHREAD_TSD_KEY_INUSE; - pthread_setspecific((pthread_key_t) k, NULL); - - if (key->in_use <= 0) - { - /* This is the last thread to use this - deleted key. It can now be made available - for re-use. - */ - key->status = _PTHREAD_TSD_KEY_REUSE; - _pthread_key_reuse[_pthread_key_reuse_top++] = k; - } - else - { - key->status = _PTHREAD_TSD_KEY_DELETED; - } - break; - - default: - break; - } - - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - - key++; - } - - if (!dirty) - break; - } -} diff --git a/condvar.c b/condvar.c index 3dc76d4..6304391 100644 --- a/condvar.c +++ b/condvar.c @@ -5,200 +5,702 @@ * This translation unit implements condition variables and their primitives. */ -#include +/* + * Code contributed by John E. Bossom . + */ + +#include -#include #include "pthread.h" +#include "implement.h" int -pthread_condattr_init(pthread_condattr_t *attr) +pthread_condattr_init (pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a condition variable attributes object + * with default attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * + * DESCRIPTION + * Initializes a condition variable attributes object + * with default attributes. + * + * NOTES: + * 1) Use to define condition variable types + * 2) It is up to the application to ensure + * that it doesn't re-init an attribute + * without destroying it first. Otherwise + * a memory leak is created. + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ { - return (attr == NULL) ? EINVAL : 0; -} + pthread_condattr_t attr_result; + int result = 0; + + attr_result = calloc (1, sizeof (*attr_result)); + + if (attr_result == NULL) + { + result = ENOMEM; + } + + *attr = attr_result; + + return (result); + +} /* pthread_condattr_init */ -int -pthread_condattr_destroy(pthread_condattr_t *attr) -{ - return (attr == NULL) ? EINVAL : 0; -} int -pthread_condattr_setpshared(pthread_condattr_t *attr, - int pshared) +pthread_condattr_destroy (pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a condition variable attributes object. + * The object can no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * + * DESCRIPTION + * Destroys a condition variable attributes object. + * The object can no longer be used. + * + * NOTES: + * 1) Does not affect condition variables created + * using 'attr' + * + * RESULTS + * 0 successfully released attr, + * EINVAL 'attr' is invalid. + * + * ------------------------------------------------------ + */ { - return (attr == NULL) ? EINVAL : ENOSYS; -} + int result = 0; + + if (attr == NULL || *attr == NULL) + { + result = EINVAL; + + } + else + { + free (*attr); + + *attr = NULL; + result = 0; + } + + return (result); + +} /* pthread_condattr_destroy */ + int -pthread_condattr_getpshared(pthread_condattr_t *attr, - int *pshared) +pthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether condition variables created with 'attr' + * can be shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_condattr_t + * + * pshared + * will be set to one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * + * DESCRIPTION + * Condition Variables created with 'attr' can be shared + * between processes if pthread_cond_t variable is allocated + * in memory shared by these processes. + * NOTES: + * 1) pshared condition variables MUST be allocated in + * shared memory. + * + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ { - return (attr == NULL) ? EINVAL : ENOSYS; -} + int result; + + if ((attr != NULL && *attr != NULL) && + (pshared != NULL)) + { + + *pshared = (*attr)->pshared; + result = 0; + + } + else + { + *pshared = PTHREAD_PROCESS_PRIVATE; + result = EINVAL; + } + + return (result); + +} /* pthread_condattr_getpshared */ + int -pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *attr) +pthread_condattr_setpshared (pthread_condattr_t * attr, int pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Mutexes created with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * pshared + * must be one of: + * + * PTHREAD_PROCESS_SHARED + * May be shared if in shared memory + * + * PTHREAD_PROCESS_PRIVATE + * Cannot be shared. + * + * DESCRIPTION + * Mutexes creatd with 'attr' can be shared between + * processes if pthread_mutex_t variable is allocated + * in memory shared by these processes. + * + * NOTES: + * 1) pshared mutexes MUST be allocated in shared + * memory. + * + * 2) The following macro is defined if shared mutexes + * are supported: + * _POSIX_THREAD_PROCESS_SHARED + * + * RESULTS + * 0 successfully set attribute, + * EINVAL 'attr' or pshared is invalid, + * ENOSYS PTHREAD_PROCESS_SHARED not supported, + * + * ------------------------------------------------------ + */ { - /* Ensure we have a valid cond_t variable. */ - if (cv == NULL) + int result; + + if ((attr != NULL && *attr != NULL) && + ((pshared == PTHREAD_PROCESS_SHARED) || + (pshared == PTHREAD_PROCESS_PRIVATE))) { - return EINVAL; - } - /* Initialize the count to 0. */ - cv->waiters_count = 0; - /* Initialize the "mutex". FIXME: Check attributes arg. */ - pthread_mutex_init(&cv->waiters_count_lock, NULL); + if (pshared == PTHREAD_PROCESS_SHARED) + { - /* Create an auto-reset event. */ - cv->events[SIGNAL] = CreateEvent (NULL, /* no security */ - FALSE, /* auto-reset event */ - FALSE, /* non-signaled initially */ - NULL); /* unnamed */ +#if !defined( _POSIX_THREAD_PROCESS_SHARED ) + result = ENOSYS; + pshared = PTHREAD_PROCESS_PRIVATE; +#else + result = 0; - /* Create a manual-reset event. */ - cv->events[BROADCAST] = CreateEvent (NULL, /* no security */ - TRUE, /* manual-reset */ - FALSE, /* non-signaled initially */ - NULL); /* unnamed */ +#endif /* _POSIX_THREAD_PROCESS_SHARED */ - return 0; -} + } + else + { + result = 0; + } + (*attr)->pshared = pshared; + + } + else + { + result = EINVAL; -/* This is an internal routine that allows the functions `pthread_cond_wait' and - `pthread_cond_timedwait' to share implementations. The `abstime' - parameter to this function is in millisecond units (or INFINITE). */ + } + + return (result); + +} /* pthread_condattr_setpshared */ -static int -cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex, DWORD abstime) + +int +pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes a condition variable. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * attr + * specifies optional creation attributes. + * + * + * DESCRIPTION + * This function initializes a condition variable. + * + * RESULTS + * 0 successfully created condition variable, + * EINVAL 'attr' is invalid, + * EAGAIN insufficient resources (other than + * memory, + * ENOMEM insufficient memory, + * EBUSY 'cond' is already initialized, + * + * ------------------------------------------------------ + */ { - int result, last_waiter; + int result = EAGAIN; + pthread_cond_t cv; - /* Ensure we have a valid cond_t variable. */ - if (cv == NULL) + if ((attr != NULL && *attr != NULL) && + ((*attr)->pshared == PTHREAD_PROCESS_SHARED)) { - return EINVAL; - } + /* + * Creating condition variable that can be shared between + * processes. + */ + result = ENOSYS; - /* CANCELATION POINT */ - pthread_testcancel(); + goto FAIL0; + } - /* Avoid race conditions. */ - pthread_mutex_lock(&cv->waiters_count_lock); - cv->waiters_count++; - pthread_mutex_unlock(&cv->waiters_count_lock); + cv = (pthread_cond_t) calloc (1, sizeof (*cv)); - /* It's okay to release the mutex here since Win32 manual-reset - events maintain state when used with SetEvent(). This avoids the - "lost wakeup" bug. */ + if (cv != NULL) + { + result = ENOMEM; + goto FAIL0; + } - pthread_mutex_unlock(mutex); + cv->waiters = 0; + cv->wasBroadcast = FALSE; - /* Wait for either event to become signaled due to - pthread_cond_signal() being called or pthread_cond_broadcast() - being called. */ - - result = WaitForMultipleObjects (2, cv->events, FALSE, abstime); + if (sem_init (&(cv->sema), 0, 1) != 0) + { + goto FAIL0; + } + if (pthread_mutex_init (&(cv->waitersLock), NULL) != 0) + { + goto FAIL1; + } - pthread_mutex_lock (&cv->waiters_count_lock); - cv->waiters_count--; - last_waiter = cv->waiters_count == 0; - pthread_mutex_unlock (&cv->waiters_count_lock); + cv->waitersDone = CreateEvent ( + 0, + (int) FALSE, /* manualReset */ + (int) FALSE, /* setSignaled */ + NULL); - /* Some thread called pthread_cond_broadcast(). */ - if ((result == WAIT_OBJECT_0 + BROADCAST) && last_waiter) + if (cv->waitersDone == NULL) { - /* We're the last waiter to be notified, so reset the manual - event. */ - ResetEvent(cv->events[BROADCAST]); + goto FAIL2; } - /* Reacquire the mutex. */ - pthread_mutex_lock(mutex); + result = 0; - return 0; -} + goto DONE; -int -pthread_cond_wait(pthread_cond_t *cv, - pthread_mutex_t *mutex) -{ - return cond_wait(cv, mutex, INFINITE); -} + /* + * ------------- + * Failure Code + * ------------- + */ +FAIL2: + (void) pthread_mutex_destroy (&(cv->waitersLock)); + +FAIL1: + (void) sem_destroy (&(cv->sema)); + free (cv); + cv = NULL; + +FAIL0: +DONE: + *cond = cv; + return (result); + +} /* pthread_cond_init */ -/* Assume that our configure script will test for the existence of - `struct timespec' and define it according to POSIX if it isn't - found. This will enable people to use this implementation - without necessarily needing Cygwin32. */ int -pthread_cond_timedwait(pthread_cond_t *cv, - pthread_mutex_t *mutex, - const struct timespec *abstime) +pthread_cond_destroy (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function destroys a condition variable + * + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function destroys a condition variable. + * + * NOTES: + * 1) Safest after wakeup from 'cond', when + * no other threads will wait. + * + * RESULTS + * 0 successfully released condition variable, + * EINVAL 'cond' is invalid, + * EBUSY 'cond' is in use, + * + * ------------------------------------------------------ + */ { - DWORD msecs; - - /* Calculate the number of milliseconds in abstime. */ - msecs = abstime->tv_sec * 1000; - msecs += abstime->tv_nsec / 1000000; + int result = 0; + pthread_cond_t cv; + + if (cond != NULL && *cond != NULL) + { + cv = *cond; + + (void) sem_destroy (&(cv->sema)); + (void) pthread_mutex_destroy (&(cv->waitersLock)); + (void) CloseHandle (cv->waitersDone); + + free (cv); - return cond_wait(cv, mutex, msecs); + *cond = NULL; + } + + return (result); } -int -pthread_cond_broadcast (pthread_cond_t *cv) +int +pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a condition variable until + * awakened by a signal or broadcast. + * + * Caller MUST be holding the mutex lock; the + * lock is released and the caller is blocked waiting + * on 'cond'. When 'cond' is signaled, the mutex + * is re-acquired before returning to the caller. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * mutex + * pointer to an instance of pthread_mutex_t + * + * + * DESCRIPTION + * This function waits on a condition variable until + * awakened by a signal or broadcast. + * + * NOTES: + * 1) The function must be called with 'mutex' LOCKED + * by the calling thread, or undefined behaviour + * will result. + * + * 2) This routine atomically releases 'mutex' and causes + * the calling thread to block on the condition variable. + * The blocked thread may be awakened by + * pthread_cond_signal or + * pthread_cond_broadcast. + * + * Upon successful completion, the 'mutex' has been locked and + * is owned by the calling thread. + * + * RESULTS + * 0 caught condition; mutex released, + * EINVAL 'cond' or 'mutex' is invalid, + * EINVAL different mutexes for concurrent waits, + * EINVAL mutex is not held by the calling thread, + * + * ------------------------------------------------------ + */ { - int have_waiters; + int result = 0; + pthread_cond_t cv; + int lastWaiter; + + cv = *cond; + + /* + * OK to increment cv->waiters because the caller locked 'mutex' + * + * FIXME: This is true. However, it is technically possible to call cond_wait + * on this cv with a different mutex. The standard leaves the result of such an + * action as undefined. (RPJ) + */ + cv->waiters++; + + /* + * We keep the lock held just long enough to increment the count of + * waiters by one (above). + * Note that we can't keep it held across the + * call to sem_wait since that will deadlock other calls + * to pthread_cond_signal + */ + if ((result = pthread_mutex_unlock (mutex)) == 0) + { + /* + * Wait to be awakened by + * pthread_cond_signal, or + * pthread_cond_broadcast + * + * Note: + * sem_wait is a cancellation point, hence providing the + * mechanism for making pthread_cond_wait a cancellation + * point. We use the cleanup mechanism to ensure we + * re-lock the mutex if we are cancelled. + */ + pthread_cleanup_push (pthread_mutex_lock, mutex); + + result = sem_wait (&(cv->sema)); + + pthread_cleanup_pop (0); + } - /* Ensure we have a valid cond_t variable. */ - if (cv == NULL) + if ((result = pthread_mutex_lock (&(cv->waitersLock))) == 0) { - return EINVAL; + /* + * By making the waiter responsible for decrementing + * its count we don't have to worry about having an internal + * mutex. + */ + cv->waiters--; + + lastWaiter = cv->wasBroadcast && (cv->waiters == 0); + + result = pthread_mutex_unlock (&(cv->waitersLock)); + } + + if (result == 0) + { + if (lastWaiter) + { + /* + * If we are the last waiter on this broadcast + * let the thread doing the broadcast proceed + */ + if (!SetEvent (cv->waitersDone)) + { + result = EINVAL; + } + } } - /* Avoid race conditions. */ - pthread_mutex_lock (&cv->waiters_count_lock); - have_waiters = (cv->waiters_count > 0); - pthread_mutex_unlock (&cv->waiters_count_lock); + /* + * We must always regain the external mutex, even when + * errors occur because that's the guarantee that we give + * to our callers + */ + (void) pthread_mutex_lock (mutex); + + + return (result); - if (have_waiters) { - SetEvent(cv->events[BROADCAST]); - } +} /* pthread_cond_wait */ - return 0; + +int +pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes an unnamed semaphore. the + * initial value of the semaphore is 'value' + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * + * DESCRIPTION + * This function initializes an unnamed semaphore. The + * initial value of the semaphore is set to 'value'. + * + * RESULTS + * 0 successfully created semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSPC a required resource has been exhausted, + * ENOSYS semaphores are not supported, + * EPERM the process lacks appropriate privilege + * + * ------------------------------------------------------ + */ +{ + int result = 0; + /* + * NOT IMPLEMENTED YET!!! + */ + return (result); } -int -pthread_cond_signal (pthread_cond_t *cv) + +int +pthread_cond_signal (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function signals a condition variable, waking + * one waiting thread. + * If SCHED_FIFO or SCHED_RR policy threads are waiting + * the highest priority waiter is awakened; otherwise, + * an unspecified waiter is awakened. + * + * PARAMETERS + * cond + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function signals a condition variable, waking + * one waiting thread. + * If SCHED_FIFO or SCHED_RR policy threads are waiting + * the highest priority waiter is awakened; otherwise, + * an unspecified waiter is awakened. + * + * NOTES: + * 1) Use when any waiter can respond and only one need + * respond (all waiters being equal). + * + * 2) This function MUST be called under the protection + * of the SAME mutex that is used with the condition + * variable being signaled; OTHERWISE, the condition + * variable may be signaled between the test of the + * associated condition and the blocking + * pthread_cond_signal. + * This can cause an infinite wait. + * + * RESULTS + * 0 successfully signaled condition, + * EINVAL 'cond' is invalid, + * + * ------------------------------------------------------ + */ { - int have_waiters; + int result = 0; + pthread_cond_t cv = *cond; - /* Ensure we have a valid cond_t variable. */ - if (cv == NULL) + /* + * If there aren't any waiters, then this is a no-op. + */ + if (cv->waiters > 0) { - return EINVAL; - } - /* Avoid race conditions. */ - pthread_mutex_lock (&cv->waiters_count_lock); - have_waiters = (cv->waiters_count > 0); - pthread_mutex_unlock (&cv->waiters_count_lock); + result = sem_post (&(cv->sema)); + } - if (have_waiters) { - SetEvent(cv->events[SIGNAL]); - } + return (result); - return 0; -} +} /* pthread_cond_signal */ int -pthread_cond_destroy(pthread_cond_t *cv) +pthread_cond_broadcast (pthread_cond_t * cond) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function broadcasts the condition variable, + * waking all current waiters. + * + * PARAMETERS + * sem + * pointer to an instance of pthread_cond_t + * + * + * DESCRIPTION + * This function initializes an unnamed semaphore. The + * initial value of the semaphore is set to 'value'. + * + * NOTES: + * 1) This function MUST be called under the protection + * of the SAME mutex that is used with the condition + * variable being signaled; OTHERWISE, the condition + * variable may be signaled between the test of the + * associated condition and the blocking pthread_cond_wait. + * This can cause an infinite wait. + * + * 2) Use when more than one waiter may respond to + * predicate change or if any waiting thread may + * not be able to respond + * + * RESULTS + * 0 successfully created semaphore, + * EINVAL 'cond' is invalid + * ENOSPC a required resource has been exhausted, + * + * ------------------------------------------------------ + */ { - if (cv == NULL) + int result = 0; + pthread_cond_t cv = *cond; + int i; + + cv->wasBroadcast = TRUE; + + /* + * Wake up all waiters + */ + for (i = cv->waiters; i > 0 && result == 0; i--) { - return EINVAL; + + result = sem_post (&(cv->sema)); } - return pthread_mutex_destroy(&cv->waiters_count_lock); + if (result == 0) + { + /* + * Wait for all the awakened threads to acquire their part of + * the counting semaphore + */ + if (WaitForSingleObject (cv->waitersDone, INFINITE) != + WAIT_OBJECT_0) + { + + result = 0; + + } + else + { + result = EINVAL; + } + + } + + return (result); } + +/* */ + diff --git a/create.c b/create.c index 6119198..37e1c61 100644 --- a/create.c +++ b/create.c @@ -6,145 +6,142 @@ * thread. */ -#include - -#include -#include -#include - #include "pthread.h" #include "implement.h" -unsigned -STDCALL _pthread_start_call(void * us_arg) -{ - /* We're now in a running thread. Any local variables here are on - this thread's private stack so we're safe to leave data in them - until we leave. */ - pthread_t us; - - /* FIXME: Needs to be a malloc(PTHREAD_KEYS_MAX) otherwise changing - _PTHREAD_MAX_KEYS in a later version of the DLL will break older apps. - */ - void * keys[_PTHREAD_MAX_KEYS]; - - unsigned (*func)(void *); - void * arg; - unsigned ret; - - us = (pthread_t) us_arg; - - memset(keys, 0, sizeof(keys)); - - (void) TlsSetValue(_pthread_threadID_TlsIndex, (LPVOID) us); - (void) TlsSetValue(_pthread_TSD_keys_TlsIndex, (LPVOID) keys); +/* + * Code contributed by John E. Bossom . + */ - /* FIXME: For now, if priority setting fails then at least ensure - that our records reflect true reality. */ - if (SetThreadPriority((HANDLE) us->win32handle, us->attr.priority) == FALSE) +int +pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(*start) (void *), + void *arg) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a thread running the start function, + * passing it the parameter value, 'arg'. + * + * PARAMETERS + * tid + * pointer to an instance of pthread_t + * + * attr + * optional pointer to an instance of pthread_attr_t + * + * start + * pointer to the starting routine for the new thread + * + * arg + * optional parameter passed to 'start' + * + * + * DESCRIPTION + * This function creates a thread running the start function, + * passing it the parameter value, 'arg'. The 'attr' + * argument specifies optional creation attributes. + * The thread is identity of the new thread is returned + * as 'tid' + * + * RESULTS + * 0 successfully created thread, + * EINVAL attr invalid, + * EAGAIN insufficient resources. + * + * ------------------------------------------------------ + */ +{ + pthread_t thread; + int result = EAGAIN; + int run = TRUE; + ThreadParms *parms; + long stackSize; + + if ((thread = (pthread_t) calloc (1, sizeof (*thread))) == + NULL) + { + goto FAIL0; + } + thread->cancelEvent = + CreateEvent ( + 0, + (int) TRUE, /* manualReset */ + (int) FALSE, /* setSignaled */ + NULL); + + if (thread->cancelEvent == NULL) { - us->attr.priority = GetThreadPriority((HANDLE) us->win32handle); + goto FAIL0; } - func = us->call.routine; - arg = us->call.arg; + if ((parms = (ThreadParms *) malloc (sizeof (*parms))) == + NULL) + { + goto FAIL0; + } - ret = (*func)(arg); + parms->tid = thread; + parms->start = start; + parms->arg = arg; - _pthread_exit(us, NULL, ret); + if (attr != NULL && *attr != NULL) + { + stackSize = (*attr)->stacksize; + thread->detachState = (*attr)->detachstate; + } + else + { + /* + * Default stackSize + */ + stackSize = 0; + } - /* Never Reached */ - return 0; -} + thread->state = run + ? PThreadStateInitial + : PThreadStateSuspended; -int -pthread_create(pthread_t *thread, - const pthread_attr_t *attr, - void * (*start_routine) (void *), - void * arg) -{ - HANDLE handle = (HANDLE) NULL; - unsigned flags; - void * security = NULL; - DWORD threadID; - pthread_attr_t * attr_copy; - pthread_t new_thread; - /* Success unless otherwise set. */ - int ret; + thread->keys = NULL; + thread->threadH = (HANDLE) + _beginthreadex ( + (void *) NULL, /* No security info */ + (unsigned) stackSize, /* default stack size */ + (unsigned (__stdcall *) (void *)) _pthread_threadStart, + parms, + (unsigned) run ? 0 : CREATE_SUSPENDED, + (unsigned *) &(thread->thread)); - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); + result = (thread->threadH != 0) ? 0 : EAGAIN; - ret = _pthread_new_thread(&new_thread); + /* + * Fall Through Intentionally + */ - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ + /* + * ------------ + * Failure Code + * ------------ + */ - if (ret == 0) +FAIL0: + if (result != 0) { - attr_copy = &(new_thread->attr); - - /* Map given attributes otherwise just use default values. */ - if (attr != NULL) - { - if (attr_copy->stacksize == 0) - { - attr_copy->stacksize = PTHREAD_STACK_MIN; - } - - attr_copy->detachedstate = attr->detachedstate; - attr_copy->priority = attr->priority; - -#if HAVE_SIGSET_T - memcpy(&(attr_copy->sigmask), &(attr->sigmask), sizeof(sigset_t)); -#endif /* HAVE_SIGSET_T */ - } - - /* We call a generic wrapper which then calls the start routine. */ - new_thread->call.routine = (unsigned (*)(void *)) start_routine; - new_thread->call.arg = arg; - - /* Start running, not suspended. */ - flags = 0; - handle = (HANDLE) _beginthreadex(security, - attr_copy->stacksize, - _pthread_start_call, - (void *) new_thread, - flags, - &threadID); + _pthread_threadDestroy (thread); + thread = NULL; - if (handle == (HANDLE) NULL) + if (parms != NULL) { - ret = EAGAIN; + free (parms); } } - else - { - ret = EAGAIN; - } - - if (ret == 0) - { - /* Let the caller know the thread handle. */ - new_thread->win32handle = handle; - new_thread->ptstatus = _PTHREAD_INUSE; - *thread = new_thread; - } - else - { - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); - - /* Remove the failed thread entry. */ - _pthread_delete_thread(new_thread); - - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ - } + *tid = thread; - return ret; -} + return (result); +} /* pthread_create */ +/* */ diff --git a/dll.c b/dll.c index bb3d7ef..dc1788f 100644 --- a/dll.c +++ b/dll.c @@ -5,26 +5,10 @@ * This translation unit implements DLL initialisation. */ -/* We use the DLL entry point function to set up per thread storage - specifically to hold the threads own thread ID. - - The thread ID is stored by _pthread_start_call(). - - The thread ID is retrieved by pthread_self(). - - */ - -#include #include #include "pthread.h" #include "implement.h" -/* Global index for TLS data. */ -DWORD _pthread_threadID_TlsIndex; - -/* Global index for thread TSD key array. */ -DWORD _pthread_TSD_keys_TlsIndex; - /* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */ BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL; @@ -32,49 +16,88 @@ BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL; /* Handle to kernel32.dll */ static HINSTANCE _pthread_h_kernel32; -BOOL WINAPI PthreadsEntryPoint(HINSTANCE dllHandle, - DWORD reason, - LPVOID situation) +#ifdef _WIN32 +/* + * lpvReserved yields an unreferenced formal parameter; + * ignore it + */ +#pragma warning( disable : 4100 ) +#endif + +BOOL WINAPI +DllMain ( + HINSTANCE hinstDll, + DWORD fdwReason, + LPVOID lpvReserved +) { - + BOOL result = TRUE; - switch (reason) + switch (fdwReason) { - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - break; case DLL_PROCESS_ATTACH: - /* Set up per thread thread ID storage. */ - _pthread_threadID_TlsIndex = TlsAlloc(); - - if (_pthread_threadID_TlsIndex == 0xFFFFFFFF) - { - return FALSE; - } - - /* Set up per thread TSD key array pointer. */ - _pthread_TSD_keys_TlsIndex = TlsAlloc(); - - if (_pthread_TSD_keys_TlsIndex == 0xFFFFFFFF) - { - return FALSE; - } + /* + * The DLL is being mapped into the process's address space + */ + result = _pthread_processInitialize (); /* Load KERNEL32 and try to get address of TryEnterCriticalSection */ _pthread_h_kernel32 = LoadLibrary(TEXT("KERNEL32.DLL")); - _pthread_try_enter_critical_section = (void *) GetProcAddress(_pthread_h_kernel32, "TryEnterCriticalSection"); + _pthread_try_enter_critical_section = + (void *) GetProcAddress(_pthread_h_kernel32, + "TryEnterCriticalSection"); break; - case DLL_PROCESS_DETACH: - (void) TlsFree(_pthread_TSD_keys_TlsIndex); - (void) TlsFree(_pthread_threadID_TlsIndex); - (void) FreeLibrary(_pthread_h_kernel32); + case DLL_THREAD_ATTACH: + /* + * A thread is being created + */ + result = TRUE; break; - default: - return FALSE; + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + /* + * A thread is exiting cleanly + * NOTE: The "main" thread detaches using + * DLL_PROCESS_DETACH + */ + { + pthread_t self; + + if (_pthread_processInitialized) + { + self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey); + + /* + * Detached threads have their resources automatically + * cleaned up upon exit (others must be 'joined' + */ + if (self != NULL && + self->detachState == PTHREAD_CREATE_DETACHED) + { + + pthread_setspecific (_pthread_selfThreadKey, NULL); + + _pthread_threadDestroy (self); + } + + if (fdwReason == DLL_PROCESS_DETACH) + { + /* + * The DLL is being unmapped into the process's address space + */ + _pthread_processTerminate (); + } + } + + (void) FreeLibrary(_pthread_h_kernel32); + + result = TRUE; + } + break; } + return (result); - return TRUE; -} +} /* DllMain */ diff --git a/exit.c b/exit.c index 3472dca..5177e88 100644 --- a/exit.c +++ b/exit.c @@ -6,69 +6,44 @@ * a thread. */ -#include -#include #include "pthread.h" #include "implement.h" -void -_pthread_vacuum(void) -{ - /* Run all the handlers. */ - _pthread_handler_pop_all(_PTHREAD_CLEANUP_STACK, - _PTHREAD_HANDLER_EXECUTE); - - /* Pop any atfork handlers without executing them. */ - _pthread_handler_pop_all(_PTHREAD_FORKPREPARE_STACK, - _PTHREAD_HANDLER_NOEXECUTE); - - _pthread_handler_pop_all(_PTHREAD_FORKPARENT_STACK, - _PTHREAD_HANDLER_NOEXECUTE); - - _pthread_handler_pop_all(_PTHREAD_FORKCHILD_STACK, - _PTHREAD_HANDLER_NOEXECUTE); -} +/* + * Code contributed by John E. Bossom . + */ -void -_pthread_exit(pthread_t thread, void * value, int return_code) +int +pthread_exit (void *value_ptr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function terminates the calling thread, returning + * the value 'value_ptr' to any joining thread. + * + * PARAMETERS + * value_ptr + * a generic data value (i.e. not the address of a value) + * + * + * DESCRIPTION + * This function terminates the calling thread, returning + * the value 'value_ptr' to any joining thread. + * NOTE: thread should be joinable. + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ { - int detachstate; + _pthread_callUserDestroyRoutines(pthread_getspecific(_pthread_selfThreadKey)); - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); + _endthreadex ((unsigned) value_ptr); - /* Copy value into the thread entry so it can be given - to any joining threads. */ - thread->joinvalueptr = value; + return (0); - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ +} /* pthread_exit */ - _pthread_vacuum(); +/* */ - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); - - /* Remove the thread entry on exit only if the thread is detached - AND there are no waiting joins. Otherwise the thread entry will - be deleted by the last waiting pthread_join() after this thread - has terminated. */ - - if (pthread_attr_getdetachstate(&thread->attr, &detachstate) == 0 - && detachstate == PTHREAD_CREATE_DETACHED - && thread->join_count == 0) - { - (void) _pthread_delete_thread(thread); - } - - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ - - _endthreadex(return_code); -} - -void -pthread_exit(void * value) -{ - _pthread_exit(pthread_self(), value, 0); -} diff --git a/fork.c b/fork.c index 3a25c48..61da545 100644 --- a/fork.c +++ b/fork.c @@ -5,12 +5,12 @@ * Implementation of fork() for POSIX threads. */ -/* FIXME! */ -#define ENOMEM 0 #include "pthread.h" #include "implement.h" +#if 0 /* Pre Bossom */ + int pthread_atfork(void (*prepare)(void), void (*parent)(void), @@ -139,3 +139,5 @@ fork() } #endif /* HAVE_PID_T && HAVE_FORK */ + +#endif /* Pre Bossom */ diff --git a/global.c b/global.c index a2ca988..56a26f3 100644 --- a/global.c +++ b/global.c @@ -6,78 +6,12 @@ * as a whole. */ -#include -#include #include "pthread.h" #include "implement.h" -/* POSIX run-time invariant values. (Currently POSIX minimum values) - Making these constants will mean that applications remain binary - compatible between versions of the DLL. +int _pthread_processInitialized = FALSE; +pthread_key_t _pthread_selfThreadKey = NULL; +pthread_key_t _pthread_cleanupKey = NULL; - FIXME: There are still places in the package that break this. -*/ -const int _POSIX_THREAD_THREADS_MAX = _PTHREAD_MAX_THREADS; -const int _POSIX_THREAD_DESTRUCTOR_ITERATIONS = 4; -const int _POSIX_THREAD_KEYS_MAX = _PTHREAD_MAX_KEYS; - - -const int _pthread_create_joinable = 0; -const int _pthread_create_detached = 1; - -/* Cancelability attributes */ -const int _pthread_cancel_enable = 0; -const int _pthread_cancel_disable = 1; - -const int _pthread_cancel_asynchronous = 0; -const int _pthread_cancel_deferred = 1; - - -/* Declare variables which are global to all threads in the process. */ - -pthread_mutex_t _pthread_table_mutex = PTHREAD_MUTEX_INITIALIZER; - -DWORD _pthread_threads_count = 0; - -/* Per thread management storage. See comments in private.c */ -/* An array of struct _pthread */ -_pthread_t _pthread_virgins[_PTHREAD_MAX_THREADS]; - -/* Index to the next available previously unused struct _pthread */ -int _pthread_virgin_next = 0; - -/* An array of pointers to struct _pthread */ -pthread_t _pthread_reuse[_PTHREAD_MAX_THREADS]; - -/* Index to the first available reusable pthread_t. */ -int _pthread_reuse_top = -1; - -/* An array of pointers to struct _pthread indexed by hashing - the Win32 handle. */ -pthread_t _pthread_win32handle_map[_PTHREAD_MAX_THREADS]; - -/* Per thread mutex locks. */ -pthread_mutex_t _pthread_threads_mutex_table[_PTHREAD_MAX_THREADS]; - -/* Global TSD key array. */ -_pthread_tsd_key_t _pthread_tsd_key_table[_PTHREAD_MAX_KEYS]; - -/* Mutex lock for TSD operations */ -pthread_mutex_t _pthread_tsd_mutex = PTHREAD_MUTEX_INITIALIZER; - -/* Index to the next available TSD key. */ -int _pthread_tsd_key_next = 0; - -/* An array of pthread_key_t */ -pthread_key_t _pthread_key_virgins[_PTHREAD_MAX_KEYS]; - -/* Index to the next available previously unused pthread_key_t */ -int _pthread_key_virgin_next = 0; - -/* An array of pthread_key_t */ -pthread_key_t _pthread_key_reuse[_PTHREAD_MAX_KEYS]; - -/* Index to the first available reusable pthread_key_t. */ -int _pthread_key_reuse_top; diff --git a/implement.h b/implement.h index db6b5a2..6bcedf6 100644 --- a/implement.h +++ b/implement.h @@ -1,197 +1,175 @@ /* * implement.h * - * Implementation specific (non API) stuff. + * Definitions that don't need to be public. + * + * Keeps all the internals out of pthread.h */ #ifndef _IMPLEMENT_H #define _IMPLEMENT_H -/* Use internally to initialise const ints and thread admin array sizes. */ -#define _PTHREAD_MAX_THREADS 128 -#define _PTHREAD_MAX_KEYS 128 - -#define _PTHREAD_HASH_INDEX(x) (((ULONG) x) % PTHREAD_THREADS_MAX) - -enum { - _PTHREAD_NEW, - _PTHREAD_INUSE, - _PTHREAD_EXITED, - _PTHREAD_REUSE -}; - -enum { - _PTHREAD_TSD_KEY_DELETED, - _PTHREAD_TSD_KEY_INUSE, - _PTHREAD_TSD_KEY_REUSE -}; - -#define _PTHREAD_VALID(T) \ - ((T) != NULL \ - && ((T)->ptstatus == _PTHREAD_NEW \ - || (T)->ptstatus == _PTHREAD_INUSE)) +/* + * Code contributed by John E. Bossom . + */ -/* Handler execution flags. */ -#define _PTHREAD_HANDLER_NOEXECUTE 0 -#define _PTHREAD_HANDLER_EXECUTE 1 -/* Special value to mark attribute objects as valid. */ -#define _PTHREAD_ATTR_VALID 0xC0FFEE +typedef struct ThreadParms ThreadParms; +typedef struct ThreadKeyAssoc ThreadKeyAssoc; -/* General description of a handler function on a stack. */ -typedef struct _pthread_handler_node _pthread_handler_node_t; -struct _pthread_handler_node { - _pthread_handler_node_t * next; - void (* routine)(void *); - void * arg; +struct ThreadParms { + pthread_t tid; + void *(*start) (void *); + void *arg; }; -/* TSD key element. */ -typedef struct _pthread_tsd_key _pthread_tsd_key_t; -struct _pthread_tsd_key { - int in_use; - int status; - void (* destructor)(void *); +struct ThreadKeyAssoc { + /* + * Purpose: + * This structure creates an association between a + * thread and a key. + * It is used to implement the implicit invocation + * of a user defined destroy routine for thread + * specific data registered by a user upon exiting a + * thread. + * + * Attributes: + * lock + * protects access to the rest of the structure + * + * thread + * reference to the thread that owns the association. + * As long as this is not NULL, the association remains + * referenced by the pthread_t. + * + * key + * reference to the key that owns the association. + * As long as this is not NULL, the association remains + * referenced by the pthread_key_t. + * + * nextKey + * The pthread_t->keys attribute is the head of a + * chain of associations that runs through the nextKey + * link. This chain provides the 1 to many relationship + * between a pthread_t and all pthread_key_t on which + * it called pthread_setspecific. + * + * nextThread + * The pthread_key_t->threads attribute is the head of + * a chain of assoctiations that runs through the + * nextThreads link. This chain provides the 1 to many + * relationship between a pthread_key_t and all the + * PThreads that have called pthread_setspecific for + * this pthread_key_t. + * + * + * Notes: + * 1) As long as one of the attributes, thread or key, is + * not NULL, the association is being referenced; once + * both are NULL, the association must be released. + * + * 2) Under WIN32, an association is only created by + * pthread_setspecific if the user provided a + * destroyRoutine when they created the key. + * + * + */ + pthread_mutex_t lock; + pthread_t thread; + pthread_key_t key; + ThreadKeyAssoc *nextKey; + ThreadKeyAssoc *nextThread; }; -/* Stores a thread call routine and argument. */ -typedef struct { - unsigned (*routine)(void *); - void * arg; -} _pthread_call_t; - -/* Macro to compute the address of a given handler stack. */ -#define _PTHREAD_STACK(stack) \ - ((_pthread_handler_node_t **) &(pthread_self()->cleanupstack) + stack); - -/* Macro to compute the table index of a thread entry from it's entry - address. */ -#define _PTHREAD_THREADS_TABLE_INDEX(this) \ - ((_pthread_threads_table_t *) this - \ - (_pthread_threads_table_t *) _pthread_threads_threads_table) - -/* Macro to compute the address of a per-thread mutex lock. */ -#define _PTHREAD_THREAD_MUTEX(this) \ - (&_pthread_threads_mutex_table[_PTHREAD_THREADS_TABLE_INDEX(this)]) - -/* An element in the thread table. */ -typedef struct _pthread _pthread_t; - -/* Keep the old typedef until we've updated all source files. */ -typedef struct _pthread _pthread_threads_thread_t; - -/* Related constants */ -struct _pthread { - HANDLE win32handle; - int ptstatus; /* _PTHREAD_EXITED - _PTHREAD_REUSABLE */ - pthread_attr_t attr; - _pthread_call_t call; - int cancel_pending; - int cancelstate; /* PTHREAD_CANCEL_DISABLE - PTHREAD_CANCEL_ENABLE */ - - int canceltype; /* PTHREAD_CANCEL_ASYNCHRONOUS - PTHREAD_CANCEL_DEFERRED */ - void ** joinvalueptr; - int join_count; - - /* These must be kept in this order and together. */ - _pthread_handler_node_t * cleanupstack; - _pthread_handler_node_t * forkpreparestack; - _pthread_handler_node_t * forkparentstack; - _pthread_handler_node_t * forkchildstack; -}; -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Generic handler push and pop routines. */ - -int _pthread_handler_push(int stack, - int poporder, - void (*routine)(void *), - void *arg); - -void _pthread_handler_pop(int stack, - int execute); - -void _pthread_handler_pop_all(int stack, - int execute); +/* + * -------------------------------------------------------------- + * MAKE_SOFTWARE_EXCEPTION + * This macro constructs a software exception code following + * the same format as the standard Win32 error codes as defined + * in WINERROR.H + * Values are 32 bit values layed out as follows: + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * Severity Values: + */ +#define SE_SUCCESS 0x00 +#define SE_INFORMATION 0x01 +#define SE_WARNING 0x10 +#define SE_ERROR 0x11 + +#define MAKE_SOFTWARE_EXCEPTION( _severity, _facility, _exception ) \ +( (DWORD) ( ( (_severity) << 30 ) | /* Severity code */ \ + ( 1 << 29 ) | /* MS=0, User=1 */ \ + ( 0 << 28 ) | /* Reserved */ \ + ( (_facility) << 16 ) | /* Facility Code */ \ + ( (_exception) << 0 ) /* Exception Code */ \ + ) ) -void _pthread_destructor_run_all(); +/* + * We choose one specific Facility/Error code combination to + * identify our software exceptions vs. WIN32 exceptions. + * We store our actual component and error code within + * the optional information array. + */ +#define EXCEPTION_PTHREAD_SERVICES \ + MAKE_SOFTWARE_EXCEPTION( SE_ERROR, \ + PTHREAD_SERVICES_FACILITY, \ + PTHREAD_SERVICES_ERROR ) -/* Primitives to manage threads table entries. */ -int _pthread_new_thread(pthread_t * thread); +#define PTHREAD_SERVICES_FACILITY 0xBAD +#define PTHREAD_SERVICES_ERROR 0xDEED -int _pthread_delete_thread(pthread_t thread); -/* Thread cleanup. */ +/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */ +extern BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION); -void _pthread_vacuum(void); +/* Declared in global.c */ +extern int _pthread_processInitialized; +extern pthread_key_t _pthread_selfThreadKey; +extern pthread_key_t _pthread_cleanupKey; -void _pthread_exit(pthread_t thread, void * value, int return_code); #ifdef __cplusplus -} +extern "C" { #endif /* __cplusplus */ +/* + * ===================== + * ===================== + * Forward Declarations + * ===================== + * ===================== + */ +int _pthread_processInitialize (void); -/* Global declared dll.c */ - -extern DWORD _pthread_threadID_TlsIndex; - -extern DWORD _pthread_TSD_keys_TlsIndex; - - -/* Global data declared in global.c */ - -extern pthread_mutex_t _pthread_table_mutex; - -extern DWORD _pthread_threads_count; - -/* An array of struct _pthread */ -extern _pthread_t _pthread_virgins[]; - -/* Index to the next available previously unused struct _pthread */ -extern int _pthread_virgin_next; - -/* An array of pointers to struct _pthread */ -extern pthread_t _pthread_reuse[]; +void _pthread_processTerminate (void); -/* Index to the first available reusable pthread_t. */ -extern int _pthread_reuse_top; +void _pthread_threadDestroy (pthread_t tid); -/* An array of pointers to struct _pthread indexed by hashing - the Win32 handle. */ -extern pthread_t _pthread_win32handle_map[]; +void _pthread_cleanupStack (void); -/* Per thread mutex locks. */ -extern pthread_mutex_t _pthread_threads_mutex_table[]; +void *_pthread_threadStart (ThreadParms * threadParms); -/* Global TSD key array. */ -extern _pthread_tsd_key_t _pthread_tsd_key_table[]; +void _pthread_callUserDestroyRoutines (pthread_t thread); -/* Mutex lock for TSD operations */ -extern pthread_mutex_t _pthread_tsd_mutex; +int _pthread_tkAssocCreate (ThreadKeyAssoc ** assocP, + pthread_t thread, + pthread_key_t key); -/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */ -extern BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION); +void _pthread_tkAssocDestroy (ThreadKeyAssoc * assoc); -/* An array of pthread_key_t */ -extern pthread_key_t _pthread_key_virgins[]; - -/* Index to the next available previously unused pthread_key_t */ -extern int _pthread_key_virgin_next; - -/* An array of pthread_key_t */ -extern pthread_key_t _pthread_key_reuse[]; +#ifdef __cplusplus +} +#endif /* __cplusplus */ -/* Index to the first available reusable pthread_key_t. */ -extern int _pthread_key_reuse_top; +/* */ #endif /* _IMPLEMENT_H */ diff --git a/misc.c b/misc.c index 659a4eb..f49cd51 100644 --- a/misc.c +++ b/misc.c @@ -8,6 +8,7 @@ #include #include "pthread.h" +#include "implement.h" int pthread_once(pthread_once_t *once_control, @@ -39,25 +40,209 @@ pthread_once(pthread_once_t *once_control, return 0; } +/* + * Code contributed by John E. Bossom . + */ + pthread_t -pthread_self(void) +pthread_self (void) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns a reference to the current running + * thread. + * + * PARAMETERS + * N/A + * + * + * DESCRIPTION + * This function returns a reference to the current running + * thread. + * + * RESULTS + * pthread_t reference to the current thread + * + * ------------------------------------------------------ + */ { - pthread_t ret; - /* This TLS index is allocated on DLL load by dll.c */ - extern DWORD _pthread_threadID_TlsIndex; - - ret = (pthread_t) TlsGetValue(_pthread_threadID_TlsIndex); + pthread_t self = NULL; + /* + * need to ensure there always is a self + */ - if (ret == 0) + if ((self = pthread_getspecific (_pthread_selfThreadKey)) == NULL) { - /* FIXME: Oh no! This can't happen. */ + /* + * Need to create an implicit 'self' for the currently + * executing thread. + */ + self = (pthread_t) calloc (1, sizeof (*self)); + if (self != NULL) + { + + self->implicit = 1; + self->detachState = PTHREAD_CREATE_DETACHED; + + self->thread = GetCurrentThreadId (); + self->threadH = GetCurrentThread (); + } + + pthread_setspecific (_pthread_selfThreadKey, self); } - return ret; -} + return (self); + +} /* pthread_self */ int -pthread_equal(pthread_t t1, pthread_t t2) +pthread_equal (pthread_t t1, pthread_t t2) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns zero if t1 and t2 are equal, else + * returns nonzero + * + * PARAMETERS + * t1, + * t2 + * references to an instances of thread_t + * + * + * DESCRIPTION + * This function returns zero if t1 and t2 are equal, else + * returns nonzero. + * + * RESULTS + * 0 if t1 and t2 refer to the same thread, + * non-zero t1 and t2 do not refer to the same thread + * + * ------------------------------------------------------ + */ { - return (t1 == t2); -} + int result; + + result = !((t1 == t2) || (t1->thread == t2->thread)); + + return (result); + +} /* pthread_equal */ + + +int +pthreadCancelableWait (HANDLE waitHandle) + /* + * ------------------------------------------------------------------- + * This provides an extra hook into the pthread_cancel + * mechanism that will allow you to wait on a Windows handle and make it a + * cancellation point. This function blocks until the given WIN32 handle is + * signaled or pthread_cancel has been called. It is implemented using + * WaitForMultipleObjects on 'waitHandle' and a manually reset WIN32 + * event used to implement pthread_cancel. + * + * Given this hook it would be possible to implement more of the cancellation + * points. + * ------------------------------------------------------------------- + */ +{ + int result; + pthread_t self; + HANDLE handles[2]; + DWORD nHandles = 1; + DWORD status; + + + handles[0] = waitHandle; + + if ((self = pthread_getspecific (_pthread_selfThreadKey)) != NULL) + { + /* + * Get cancelEvent handle + */ + if (self->cancelState == PTHREAD_CANCEL_ENABLE) + { + + if ((handles[1] = self->cancelEvent) != NULL) + { + nHandles++; + } + } + } + else + { + handles[1] = NULL; + } + + status = WaitForMultipleObjects ( + nHandles, + handles, + FALSE, + INFINITE); + + + if (status == WAIT_FAILED) + { + result = EINVAL; + + } + else if (status == WAIT_ABANDONED_0) + { + result = EINVAL; + + } + else + { + /* + * Either got the mutex or the cancel event + * was signaled + */ + switch (status - WAIT_OBJECT_0) + { + + case 0: + /* + * Got the mutex + */ + result = 0; + break; + + case 1: + /* + * Got cancel request + */ + ResetEvent (handles[1]); + + if (self != NULL && !self->implicit) + { + /* + * Thread started with pthread_create + */ + DWORD exceptionInformation[3]; + + exceptionInformation[0] = (DWORD) (0); + exceptionInformation[1] = (DWORD) (0); + + RaiseException ( + EXCEPTION_PTHREAD_SERVICES, + 0, + 3, + exceptionInformation); + } + + + ((void *) -1); + break; + + default: + result = EINVAL; + break; + } + } + + return (result); + +} /* pthreadCancelableWait */ + + +/* */ + diff --git a/private.c b/private.c index 32116ef..901973a 100644 --- a/private.c +++ b/private.c @@ -11,133 +11,392 @@ #include "pthread.h" #include "implement.h" -/* Thread ID management. - --------------------- +/* + * Code contributed by John E. Bossom . + */ - We started by simply mapping the Win32 thread handle directly to - pthread_t. However, in order to process pthread_join()'s, we need - to be able to keep our POSIX thread ID (pthread_t) around after the - Win32 thread has terminated. Win32 may reuse the Win32 handle during that - time, which will conflict. +int +_pthread_processInitialize (void) + /* + * ------------------------------------------------------ + * DOCPRIVATE + * This function performs process wide initialization for + * the pthread library. + * + * PARAMETERS + * N/A + * + * DESCRIPTION + * This function performs process wide initialization for + * the pthread library. + * If successful, this routine sets the global variable + * _pthread_processInitialized to TRUE. + * + * RESULTS + * TRUE if successful, + * FALSE otherwise + * + * ------------------------------------------------------ + */ +{ + _pthread_processInitialized = TRUE; - The pthread_t value is now actually the pointer to a thread struct: + /* + * Initialize Keys + */ + if ((pthread_key_create (&_pthread_selfThreadKey, NULL) != 0) || + (pthread_key_create (&_pthread_cleanupKey, NULL) != 0)) + { - typedef struct _pthread * pthread_t; + _pthread_processTerminate (); + } - which amongst other things stores the Win32 thread handle: + return (_pthread_processInitialized); + +} /* processInitialize */ + +void +_pthread_processTerminate (void) + /* + * ------------------------------------------------------ + * DOCPRIVATE + * This function performs process wide termination for + * the pthread library. + * + * PARAMETERS + * N/A + * + * DESCRIPTION + * This function performs process wide termination for + * the pthread library. + * This routine sets the global variable + * _pthread_processInitialized to FALSE + * + * RESULTS + * N/A + * + * ------------------------------------------------------ + */ +{ + if (_pthread_processInitialized) + { + + if (_pthread_selfThreadKey != NULL) + { + /* + * Release _pthread_selfThreadKey + */ + pthread_key_delete (_pthread_selfThreadKey); - struct _pthread { - HANDLE win32handle; - int ptstatus; - ... - }; + _pthread_selfThreadKey = NULL; + } + + if (_pthread_cleanupKey != NULL) + { + /* + * Release _pthread_cleanupKey + */ + pthread_key_delete (_pthread_cleanupKey); - So now whereever we need to use the Win32 handle it can be accessed - as: + _pthread_cleanupKey = NULL; + } - pthread_t T = pthread_this(); - HANDLE H; + _pthread_processInitialized = FALSE; + } - H = T->win32handle; +} /* processTerminate */ - // or (which is NOT preferred, let the compiler optimise to this). +void * +_pthread_threadStart (ThreadParms * threadParms) +{ + pthread_t tid; + void *(*start) (void *); + void *arg; - H = (HANDLE) *T; + int status; + tid = threadParms->tid; + start = threadParms->start; + arg = threadParms->arg; - POSIX Threads Table - ------------------- + free (threadParms); - Having the thread ID as a pointer to the thread struct itself - avoids the need to search the threads table in all but the initial - occasion where we create the thread. + pthread_setspecific (_pthread_selfThreadKey, tid); - Initially we used a hash function to select a free thread struct - from the table, possibly needing a walk through the table if the - hash collided with an already in-use thread. + __try + { + /* + * Run the caller's routine; + */ + (*start) (arg); + status = 0; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* + * A system unexpected exception had occurred running the user's + * routine. We get control back within this block. + */ + status = -1; + } - The scheme used now is more efficient and is done as follows: + pthread_exit ((void *) status); - We use two tables and two counters: + return ((void *) status); - struct _pthread _pthread_virgins[PTHREAD_THREADS_MAX]; - pthread_t _pthread_reuse[PTHREAD_THREADS_MAX]; +} /* threadStart */ - int _pthread_virgin_next = 0; - int _pthread_reuse_top = -1; +void +_pthread_threadDestroy (pthread_t thread) +{ + if (thread != NULL) + { + _pthread_callUserDestroyRoutines (thread); - The counter _pthread_virgin_next is an index into _pthread_virgins[], - which can be thought of as a list, and _pthread_reuse_top is an - index into _pthread_reuse[], which can be thought of as a LIFO stack. + if (thread->cancelEvent != NULL) + { + CloseHandle (thread->cancelEvent); + } - Once taken from _pthread_virgins[], used and freed threads are only - ever pushed back onto _pthread_reuse[]. + free (thread); + } - */ +} /* threadDestroy */ int -_pthread_new_thread(pthread_t * thread) +_pthread_tkAssocCreate (ThreadKeyAssoc ** assocP, + pthread_t thread, + pthread_key_t key) + /* + * ------------------------------------------------------------------- + * This routine creates an association that + * is unique for the given (thread,key) combination.The association + * is referenced by both the thread and the key. + * This association allows us to determine what keys the + * current thread references and what threads a given key + * references. + * See the detailed description + * at the beginning of this file for further details. + * + * Notes: + * 1) New associations are pushed to the beginning of the + * chain so that the internal _pthread_selfThreadKey association + * is always last, thus allowing selfThreadExit to + * be implicitly called by pthread_exit last. + * + * Parameters: + * assocP + * address into which the association is returned. + * thread + * current running thread. If NULL, then association + * is only added to the key. A NULL thread indicates + * that the user called pthread_setspecific prior + * to starting a thread. That's ok. + * key + * key on which to create an association. + * Returns: + * 0 - if successful, + * -1 - general error + * ------------------------------------------------------------------- + */ { - pthread_t new_thread; + int result; + ThreadKeyAssoc *assoc; + + /* + * Have to create an association and add it + * to both the key and the thread. + */ + assoc = (ThreadKeyAssoc *) + calloc (1, sizeof (*assoc)); - if (_pthread_reuse_top >= 0) + if (assoc == NULL) { - new_thread = _pthread_reuse[_pthread_reuse_top--]; + result = -1; + goto FAIL0; } - else + + if ((result = pthread_mutex_init (&(assoc->lock), NULL)) != + 0) { - if (_pthread_virgin_next < PTHREAD_THREADS_MAX) - { - new_thread = (pthread_t) &_pthread_virgins[_pthread_virgin_next++]; - } - else - { - return EAGAIN; - } + goto FAIL1; } - new_thread->win32handle = (HANDLE) NULL; - new_thread->ptstatus = _PTHREAD_NEW; - pthread_attr_init(&(new_thread->attr)); - new_thread->joinvalueptr = NULL; - new_thread->cancelstate = PTHREAD_CANCEL_ENABLE; - new_thread->canceltype = PTHREAD_CANCEL_DEFERRED; - new_thread->cancel_pending = FALSE; - new_thread->cleanupstack = NULL; - new_thread->forkpreparestack = NULL; - new_thread->forkparentstack = NULL; - new_thread->forkchildstack = NULL; + assoc->thread = thread; + assoc->key = key; - *thread = new_thread; - _pthread_threads_count++; + /* + * Register assoc with key + */ + if ((result = pthread_mutex_lock (&(key->threadsLock))) != + 0) + { + goto FAIL2; + } - return 0; -} + assoc->nextThread = (ThreadKeyAssoc *) key->threads; + key->threads = (void *) assoc; -int -_pthread_delete_thread(_pthread_t * thread) -{ - /* We don't check that the thread has been properly cleaned up, so - it had better be done already. */ + pthread_mutex_unlock (&(key->threadsLock)); + + if (thread != NULL) + { + /* + * Register assoc with thread + */ + assoc->nextKey = (ThreadKeyAssoc *) thread->keys; + thread->keys = (void *) assoc; + } + + *assocP = assoc; + + return (result); + + /* + * ------------- + * Failure Code + * ------------- + */ +FAIL2: + pthread_mutex_destroy (&(assoc->lock)); + +FAIL1: + free (assoc); - /* Release any keys */ +FAIL0: - _pthread_destructor_run_all(); + return (result); - /* Remove the thread entry if necessary. */ +} /* tkAssocCreate */ - if (thread != NULL - && thread->ptstatus == _PTHREAD_EXITED) + +void +_pthread_tkAssocDestroy (ThreadKeyAssoc * assoc) + /* + * ------------------------------------------------------------------- + * This routine releases all resources for the given ThreadKeyAssoc + * once it is no longer being referenced + * ie) both the key and thread have stopped referencing it. + * + * Parameters: + * assoc + * an instance of ThreadKeyAssoc. + * Returns: + * N/A + * ------------------------------------------------------------------- + */ +{ + + if ((assoc != NULL) && + (assoc->key == NULL && assoc->thread == NULL)) { - pthread_attr_destroy(&(thread->attr)); - thread->win32handle = (HANDLE) NULL; - thread->ptstatus = _PTHREAD_REUSE; - _pthread_reuse[++_pthread_reuse_top] = thread; - _pthread_threads_count--; + pthread_mutex_destroy (&(assoc->lock)); - return 0; + free (assoc); } - return EINVAL; -} +} /* tkAssocDestroy */ + + +void +_pthread_callUserDestroyRoutines (pthread_t thread) + /* + * ------------------------------------------------------------------- + * DOCPRIVATE + * + * This the routine runs through all thread keys and calls + * the destroy routines on the user's data for the current thread. + * It simulates the behaviour of POSIX Threads. + * + * PARAMETERS + * thread + * an instance of pthread_t + * + * RETURNS + * N/A + * ------------------------------------------------------------------- + */ +{ + ThreadKeyAssoc **nextP; + ThreadKeyAssoc *assoc; + + if (thread != NULL) + { + /* + * Run through all Thread<-->Key associations + * for the current thread. + * If the pthread_key_t still exits (ie the assoc->key + * is not NULL) then call the user's TSD destroy routine. + * Notes: + * If assoc->key is NULL, then the user previously called + * PThreadKeyDestroy. The association is now only referenced + * by the current thread and must be released; otherwise + * the assoc will be destroyed when the key is destroyed. + */ + nextP = (ThreadKeyAssoc **) & (thread->keys); + assoc = *nextP; + + while (assoc != NULL) + { + + if (pthread_mutex_lock (&(assoc->lock)) == 0) + { + pthread_key_t k; + if ((k = assoc->key) != NULL) + { + /* + * Key still active; pthread_key_delete + * will block on this same mutex before + * it can release actual key; therefore, + * key is valid and we can call the destroy + * routine; + */ + void *value = NULL; + + value = pthread_getspecific (k); + if (value != NULL && k->destructor != NULL) + { + + __try + { + /* + * Run the caller's cleanup routine. + */ + (*(k->destructor)) (value); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* + * A system unexpected exception had occurred + * running the user's destructor. + * We get control back within this block. + */ + } + } + } + + /* + * mark assoc->thread as NULL to indicate the + * thread no longer references this association + */ + assoc->thread = NULL; + + /* + * Remove association from the pthread_t chain + */ + *nextP = assoc->nextKey; + + pthread_mutex_unlock (&(assoc->lock)); + + _pthread_tkAssocDestroy (assoc); + + assoc = *nextP; + } + } + } + +} /* callUserDestroyRoutines */ + +/* */ + diff --git a/pthread.def b/pthread.def index 63687f5..bd294b9 100644 --- a/pthread.def +++ b/pthread.def @@ -1,12 +1,12 @@ ; pthread.def -; Last updated: $Date: 1998/10/04 23:01:59 $ +; Last updated: $Date: 1999/01/03 18:48:06 $ ; Currently unimplemented functions are commented out. LIBRARY pthread EXPORTS -pthread_atfork +;pthread_atfork pthread_attr_destroy pthread_attr_getdetachstate ;pthread_attr_getinheritsched diff --git a/pthread.h b/pthread.h index ef22d80..702028f 100644 --- a/pthread.h +++ b/pthread.h @@ -1,33 +1,199 @@ /* This is the POSIX thread API (POSIX 1003). - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This library 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -/* FIXME: do not include function prototypes for functions which are - not yet implemented. This will allow us to keep a better handle on - where we're at. */ - -#ifndef _PTHREADS_H -#define _PTHREADS_H - -/* Convert these to defined when implemented. */ -#define _POSIX_THREAD_ATTR_STACKSIZE -#ifdef _POSIX_THREAD_ATTR_STACKADDR -#undef _POSIX_THREAD_ATTR_STACKADDR + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * ------------------------------------------------------------- + * + * + * Module: pthread.h + * + * Purpose: + * Provides an implementation of PThreads based upon the + * standard: + * + * POSIX 1003.1c-1995 (POSIX.1c) + * + * Authors: + * Contributors are listed in the file "MAINTAINERS". + * + * The following functions are implemented: + * --------------------------- + * PThreads + * --------------------------- + * pthread_attr_init + * pthread_attr_destroy + * pthread_attr_getdetachstate + * pthread_attr_getstackaddr + * pthread_attr_getstacksize + * pthread_attr_setdetachstate + * pthread_attr_setstackaddr + * pthread_attr_setstacksize + * + * pthread_create + * pthread_detach + * pthread_equal + * pthread_exit + * pthread_join + * pthread_self + * sched_yield + * + * pthread_cancel + * pthread_cleanup_pop + * pthread_cleanup_push + * pthread_setcancelstate + * pthread_setcanceltype + * pthread_testcancel + * + * --------------------------- + * Thread Specific Data + * --------------------------- + * pthread_key_create + * pthread_key_delete + * pthread_setspecific + * pthread_getspecific + * + * --------------------------- + * Mutexes + * --------------------------- + * pthread_mutexattr_init + * pthread_mutexattr_destroy + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * + * pthread_mutex_init + * pthread_mutex_destroy + * pthread_mutex_lock + * pthread_mutex_trylock + * pthread_mutex_unlock + * + * --------------------------- + * Condition Variables + * --------------------------- + * pthread_condattr_init + * pthread_condattr_destroy + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * pthread_cond_init + * pthread_cond_destroy + * pthread_cond_wait + * pthread_cond_timedwait + * pthread_cond_signal + * pthread_cond_broadcast + * + * --------------------------- + * Protected Methods + * --------------------------- + * pthreadCancelableWait + * + * --------------------------- + * RealTime Scheduling: + * --------------------------- + * pthread_attr_getschedparam + * pthread_attr_setschedparam + * pthread_getschedparam + * pthread_setschedparam + * sched_get_priority_max + * sched_get_priority_min + * + * --------------------------- + * Signals: + * --------------------------- + * pthread_sigmask + * + * Limitations + * =========== + * The following functions are not implemented: + * + * --------------------------- + * RealTime Scheduling: + * --------------------------- + * pthread_attr_getinheritsched + * pthread_attr_getschedpolicy + * pthread_attr_getscope + * pthread_attr_setinheritsched + * pthread_attr_setschedpolicy + * pthread_attr_setscope + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutex_attr_getprioceiling + * pthread_mutex_attr_getprotocol + * pthread_mutex_attr_setprioceiling + * pthread_mutex_attr_setprotocol + * + * --------------------------- + * Fork Handlers: + * --------------------------- + * pthread_atfork + * + * --------------------------- + * Stdio: + * --------------------------- + * flockfile + * ftrylockfile + * funlockfile + * getc_unlocked + * getchar_unlocked + * putc_unlocked + * putchar_unlocked + * + * --------------------------- + * Thread-Safe C Runtime Library: + * --------------------------- + * readdir_r + * getgrgid_r + * getgrnam_r + * getpwuid_r + * getpwnam_r + * + * --------------------------- + * Signals: + * --------------------------- + * pthread_kill + * sigtimedwait + * sigwait + * sigwaitinfo + * + * + * ------------------------------------------------------------- + */ +#if !defined( PTHREAD_H ) +#define PTHREAD_H + +#ifdef _WIN32 +/* + * Disable following warnings when including Windows headers + * + * warning C4115: named type definition in parentheses + * warning C4116: unnamed type definition in parentheses + * warning C4127: conditional expression is constant + * warning C4201: nonstandard extension used : nameless struct/union + * warning C4214: nonstandard extension used : bit field types other than int + * warning C4514: unreferenced inline function has been removed + */ +#pragma warning( disable : 4115 4116 4127 4201 4214 4514) #endif +/* + * ----------------- + * autoconf switches + * ----------------- + */ + #if HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -58,299 +224,730 @@ struct timespec { #define SIG_SETMASK 2 #endif /* SIG_SETMASK */ -#define PTHREAD_STACK_MIN 65535 -/* Thread scheduling policies */ +#include +#include -#define SCHED_OTHER 0 -#define SCHED_FIFO 1 -#define SCHED_RR 2 - -#define SCHED_MIN SCHED_OTHER -#define SCHED_MAX SCHED_RR +#ifdef _WIN32 +/* + * Re-enable all but 4127, 4514 + */ +#pragma warning( default : 4115 4116 4201 4214) +#endif -/* Cancelation return value. - This value must be neither NULL nor the value of any - pointer to an object in memory. */ -#define PTHREAD_CANCELED ((void *) 1) +#if !defined( TRUE ) +#define TRUE !FALSE +#define FALSE 0 +#endif /* !TRUE */ -#define PTHREAD_MUTEX_INITIALIZER {0 /* ignore internals */ } -#define PTHREAD_ONCE_INIT { 0, PTHREAD_MUTEX_INITIALIZER } -typedef struct _pthread * pthread_t; -typedef struct { - int valid; - CRITICAL_SECTION cs; -} pthread_mutex_t; +#include +/* #include /**/ -typedef DWORD pthread_key_t; +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * ------------------------------------------------------------- + * + * POSIX 1003.1c-1995 Options + * =========================== + * + * _POSIX_SEMAPHORES + * Semaphores come from POSIX.1b (POSIX 1003.1b-1993) + * rather than from PThreads. This macro indicates + * that POSIX Semaphores are supported: + * sem_destroy + * sem_init + * sem_wait + * sem_trywait + * sem_post + * + * _POSIX_THREADS (set) + * If set, you can use threads + * + * _POSIX_THREAD_ATTR_STACKSIZE (set) + * If set, you can control the size of a thread's + * stack + * pthread_attr_getstacksize + * pthread_attr_setstacksize + * + * _POSIX_THREAD_ATTR_STACKADDR (not set) + * If set, you can allocate and control a thread's + * stack. If not supported, the following functions + * will return ENOSYS, indicating they are not + * supported: + * pthread_attr_getstackaddr + * pthread_attr_setstackaddr + * + * _POSIX_THREAD_PRIORITY_SCHEDULING (not set) + * If set, you can use realtime scheduling. + * Indicates the availability of: + * pthread_attr_getinheritsched + * pthread_attr_getschedparam + * pthread_attr_getschedpolicy + * pthread_attr_getscope + * pthread_attr_setinheritsched + * pthread_attr_setschedparam + * pthread_attr_setschedpolicy + * pthread_attr_setscope + * pthread_getschedparam + * pthread_setschedparam + * pthread_get_priority_max + * pthread_get_priority_min + * + * _POSIX_THREAD_PRIO_INHERIT (not set) + * If set, you can create priority inheritance + * mutexes. + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PRIO_PROTECT (not set) + * If set, you can create priority ceiling mutexes + * Indicates the availability of: + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutexattr_getprioceiling + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprioceiling + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PROCESS_SHARED (not set) + * If set, you can create mutexes and condition + * variables that can be shared with another + * process.If set, indicates the availability + * of: + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * _POSIX_THREAD_SAFE_FUNCTIONS (set) + * If set you can use the special *_r library + * functions that provide thread-safe behaviour + * + * + These functions provide both 'inherit' and/or + * 'protect' protocol, based upon these macro + * settings. + * + * POSIX 1003.1c-1995 Limits + * =========================== + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Maximum number of attempts to destroy + * a thread's thread-specific data on + * termination (must be at least 4) + * + * PTHREAD_KEYS_MAX + * Maximum number of thread-specific data keys + * available per process (must be at least 128) + * + * PTHREAD_STACK_MIN + * Minimum supported stack size for a thread + * + * PTHREAD_THREADS_MAX + * Maximum number of threads supported per + * process (must be at least 64). + * + * ------------------------------------------------------------- + */ + +/* + * POSIX Options + */ +#define _POSIX_THREADS +#define _POSIX_SEMAPHORES +#define _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_ATTR_STACKSIZE -/* Related constants */ -typedef struct { - long valid; +#if defined( KLUDGE ) +/* + * The following are not supported + */ +#define _POSIX_THREAD_ATTR_STACKADDR +#define _POSIX_THREAD_PRIORITY_SCHEDULING +#define _POSIX_THREAD_PRIO_INHERIT +#define _POSIX_THREAD_PRIO_PROTECT +#define _POSIX_THREAD_PROCESS_SHARED + +#endif /* KLUDGE */ + +/* + * POSIX Limits + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Standard states this must be at least + * 4. + * + * PTHREAD_KEYS_MAX + * WIN32 permits only 64 TLS keys per process. + * This limitation could be worked around by + * simply simulating keys. + * + * PTHREADS_STACK_MIN + * artibrarily chose 1K. By default, WIN32 + * selects 1Meg stacks. + * + * PTHREAD_THREADS_MAX + * Not documented by WIN32. Wrote a test program + * that kept creating threads until it failed + * revealed this approximate number. + * + */ +#define PTHREAD_DESTRUCTOR_ITERATIONS 4 +#define PTHREAD_KEYS_MAX 64 +#define PTHREAD_STACK_MIN 1024 +#define PTHREAD_THREADS_MAX 2019 + + + typedef struct pthread_t_ *pthread_t; + typedef struct pthread_attr_t_ *pthread_attr_t; + typedef struct pthread_once_t_ pthread_once_t; + typedef struct pthread_key_t_ *pthread_key_t; + typedef struct pthread_mutex_t_ pthread_mutex_t; + typedef struct pthread_mutexattr_t_ pthread_mutexattr_t; + typedef struct pthread_cond_t_ *pthread_cond_t; + typedef struct pthread_condattr_t_ *pthread_condattr_t; + + +/* + * ==================== + * ==================== + * POSIX Threads + * ==================== + * ==================== + */ + +/* + * pthread_attr_{get,set}detachstate + */ +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 1 + +/* + * pthread_attr{get,set}inheritsched + */ +#define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_EXPLICIT_SCHED 1 + +/* + * pthread_setcancelstate paramters + */ +#define PTHREAD_CANCEL_ENABLE 0 +#define PTHREAD_CANCEL_DISABLE 1 + +/* + * pthread_setcanceltype parameters + */ +#define PTHREAD_CANCEL_ASYNCHRONOUS 0 +#define PTHREAD_CANCEL_DEFERRED 1 + +/* + * ==================== + * ==================== + * Mutex + * ==================== + * ==================== + */ + +/* FIXME: Replace the NULL with a valid critical section initializer + * and then also change the 0 (first element) to 1. + */ +#define PTHREAD_MUTEX_INITIALIZER { 0, NULL } + + +/* + * pthread_mutexattr_{get,set}pshared + * pthread_condattr_{get,set}pshared + */ +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + + +/* + * ==================== + * ==================== + * Once Key + * ==================== + * ==================== + */ +#define PTHREAD_ONCE_INIT { 0, PTHREAD_MUTEX_INITIALIZER } + + +/* + * ==================== + * ==================== + * Condition Variable + * ==================== + * ==================== + */ +#define PTHREAD_COND_INITIALIZER { {0, 0}, 0, PTHREAD_MUTEX_INITIALIZER } + + +/* + * ==================== + * ==================== + * Opaque Structure Definitions + * ==================== + * ==================== + */ + +typedef enum { + /* + * This enumeration represents the state of the thread; + * The thread is still "alive" if the numeric value of the + * state is greater or equal "PThreadStateRunning". + */ + PThreadStateInitial = 0, /* Thread not running */ + PThreadStateRunning, /* Thread alive & kicking */ + PThreadStateSuspended, /* Thread alive but suspended */ + PThreadStateCanceling, /* Thread alive but and is */ + /* in the process of terminating */ + /* due to a cancellation request */ + PThreadStateException, /* Thread alive but exiting */ + /* due to an exception */ + PThreadStateLast +} +PThreadState; + + +typedef enum { + /* + * This enumeration represents the reason why a thread has + * terminated/is terminating. + */ + PThreadDemisePeaceful = 0, /* Death due natural causes */ + PThreadDemiseCancelled, /* Death due to user cancel */ + PThreadDemiseException, /* Death due to unhandled */ + /* exception */ + PThreadDemiseNotDead /* I'm not dead! */ +} +PThreadDemise; + + +struct pthread_t_ { + DWORD thread; + HANDLE threadH; + PThreadState state; + PThreadDemise demise; + void *exitStatus; + void *parms; + int detachState; + int cancelState; + int cancelType; + HANDLE cancelEvent; + int implicit:1; + void *keys; +}; -#ifdef _POSIX_THREAD_ATTR_STACKSIZE - size_t stacksize; /* PTHREAD_STACK_MIN */ -#endif - int detachedstate; /* PTHREAD_CREATE_DETACHED - PTHREAD_CREATE_JOINABLE */ +/* + * Special value to mark attribute objects as valid. + */ +#define _PTHREAD_ATTR_VALID 0xC4C0FFEE +struct pthread_attr_t_ { + long valid; + void *stackaddr; + size_t stacksize; + int detachstate; + int priority; #if HAVE_SIGSET_T sigset_t sigmask; #endif /* HAVE_SIGSET_T */ +}; - int priority; -} pthread_attr_t; +struct pthread_mutex_t_ { + int valid; + CRITICAL_SECTION cs; + }; + + +struct pthread_mutexattr_t_ { + int pshared; +}; -/* I don't know why this structure isn't in some kind of namespace. - According to my O'Reilly book, this is what this struct is - called. */ -struct sched_param { - int sched_priority; + +struct pthread_key_t_ { + DWORD key; + void (*destructor) (void *); + pthread_mutex_t threadsLock; + void *threads; }; -enum { SIGNAL, BROADCAST, NUM_EVENTS }; -typedef struct { - /* Signal and broadcast event HANDLEs. */ - HANDLE events[NUM_EVENTS]; +struct pthread_cond_t_ { + long waiters; /* # waiting threads */ + pthread_mutex_t waitersLock; /* Mutex that guards access to + waiter count */ + sem_t sema; /* Queue up threads waiting for the + condition to become signaled */ + HANDLE waitersDone; /* An auto reset event used by the + broadcast/signal thread to wait + for the waiting thread(s) to wake + up and get a chance at the + semaphore */ + int wasBroadcast; /* keeps track if we are signaling + or broadcasting */ +}; + - /* Count of the number of waiters. */ - unsigned waiters_count; - - /* Serialize access to waiters_count. */ - pthread_mutex_t waiters_count_lock; -} pthread_cond_t; +struct pthread_condattr_t_ { + int pshared; +}; -typedef struct { void * dummy; } pthread_condattr_t; -typedef struct { void * dummy; } pthread_mutexattr_t; -typedef struct { +struct pthread_once_t_ { unsigned short flag; pthread_mutex_t lock; -} pthread_once_t; +}; -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ -int pthread_atfork (void (*prepare)(void), - void (*parent)(void), - void (*child)(void)); +/* + * ==================== + * ==================== + * Scheduling + * ==================== + * ==================== + */ -int pthread_create(pthread_t *thread, - const pthread_attr_t *attr, - void * (*start_routine) (void *), - void * arg); +/* Thread scheduling policies */ -void pthread_exit(void *value); +#define SCHED_OTHER 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 -pthread_t pthread_self(void); +#define SCHED_MIN SCHED_OTHER +#define SCHED_MAX SCHED_RR -int pthread_equal(pthread_t t1, pthread_t t2); + struct sched_param { + int sched_priority; + }; -int pthread_join(pthread_t thread, void ** valueptr); -int pthread_detach(pthread_t thread); -int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); +/* There are three implementations of cancel cleanup. + * + * C + * C++ (as per Cygwin32 or Mingw32) + * WIN32 SEH or C++ + */ -/* Functions for manipulating thread attribute objects. */ + typedef struct _pthread_cleanup_t _pthread_cleanup_t; -int pthread_attr_init(pthread_attr_t *attr); + struct _pthread_cleanup_t + { + void (*routine) (void *); + void *arg; +#if !defined(__cplusplus) + _pthread_cleanup_t *prev; +#endif + }; + +#ifndef __cplusplus + +/* + * C implementation of PThreads cancel cleanup + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + _pthread_cleanup_t _cleanup; \ + \ + _pthread_push_cleanup( &_cleanup, (_rout), (_arg) ); \ + +#define pthread_cleanup_pop( _execute ) \ + (void) _pthread_pop_cleanup( _execute ); \ + } + +#else /* !__cplusplus */ + +#ifdef _WIN32 + /* + * WIN32 SEH version of cancel cleanup. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + _pthread_cleanup_t _cleanup; \ + \ + _cleanup.routine = (_rout); \ + _cleanup.arg = (_arg); \ + __try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + __finally \ + { \ + if( _execute || AbnormalTermination()) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } \ + } + +#else /* _WIN32 */ + + /* + * C++ (ie. Cygwin32 or Mingw32) version of cancel cleanup. + * + * Emulate try-finally behaviour. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + _pthread_cleanup_t _cleanup; \ + \ + _cleanup.routine = (_rout); \ + _cleanup.arg = (_arg); \ + try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + catch(...) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + \ + throw; \ + } \ + \ + if (_execute) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } -int pthread_attr_destroy(pthread_attr_t *attr); +#endif /* _WIN32 */ -int pthread_attr_setstacksize(pthread_attr_t *attr, - size_t stacksize); +#endif /* !__cplusplus */ -int pthread_attr_getstacksize(const pthread_attr_t *attr, - size_t *stacksize); +/* + * =============== + * =============== + * Methods + * =============== + * =============== + */ -int pthread_attr_setstackaddr(pthread_attr_t *attr, - void *stackaddr); +/* + * PThread Attribute Functions + */ +int pthread_attr_init (pthread_attr_t * attr); -int pthread_attr_getstackaddr(const pthread_attr_t *attr, - void **stackaddr); +int pthread_attr_destroy (pthread_attr_t * attr); -int pthread_attr_getschedparam(const pthread_attr_t *attr, - struct sched_param *param); +int pthread_attr_getdetachstate (const pthread_attr_t * attr, + int *detachstate); -int pthread_attr_setschedparam(pthread_attr_t *attr, - const struct sched_param *param); +int pthread_attr_getstackaddr (const pthread_attr_t * attr, + void **stackaddr); -int pthread_attr_getdetachstate(const pthread_attr_t *attr, - int *detachstate); +int pthread_attr_getstacksize (const pthread_attr_t * attr, + size_t * stacksize); -int pthread_attr_setdetachstate(pthread_attr_t *attr, - int detachstate); - -int pthread_setschedparam(pthread_t thread, - int policy, - const struct sched_param *param); +int pthread_attr_setdetachstate (pthread_attr_t * attr, + int detachstate); -int pthread_getschedparam(pthread_t thread, - int *policy, - struct sched_param *param); +int pthread_attr_setstackaddr (pthread_attr_t * attr, + void *stackaddr); -int sched_get_priority_min(int policy); +int pthread_attr_setstacksize (pthread_attr_t * attr, + size_t stacksize); -int sched_get_priority_max(int policy); +/* + * PThread Functions + */ +int pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void *(*start) (void *), + void *arg); -int pthread_setcancelstate(int state, - int *oldstate); +int pthread_detach (pthread_t tid); -int pthread_setcanceltype(int type, - int *oldtype); +int pthread_equal (pthread_t t1, + pthread_t t2); -/* Functions for manipulating cond. var. attribute objects. */ +int pthread_exit (void *value_ptr); -int pthread_condattr_init(pthread_condattr_t *attr); +int pthread_join (pthread_t thread, + void **value_ptr); -int pthread_condattr_setpshared(pthread_condattr_t *attr, - int pshared); +pthread_t pthread_self (void); -int pthread_condattr_getpshared(pthread_condattr_t *attr, - int *pshared); +int pthread_cancel (pthread_t thread); -int pthread_condattr_destroy(pthread_condattr_t *attr); +#ifndef __cplusplus +_pthread_cleanup_t *_pthread_pop_cleanup (int execute); -/* Functions for manipulating mutex attribute objects. */ +void _pthread_push_cleanup (_pthread_cleanup_t * cleanup, + void (*routine) (void *), + void *arg); +#endif /* !__cplusplus */ -int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_setcancelstate (int state, + int *oldstate); -int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); +int pthread_setcanceltype (int type, + int *oldtype); -/* Primitives for condition variables. */ +void pthread_testcancel (void); -int pthread_cond_init(pthread_cond_t *cv, - const pthread_condattr_t *attr); +int pthread_once (pthread_once_t * once_control, + void (*init_routine) (void)); -int pthread_cond_broadcast(pthread_cond_t *cv); +/* + * Thread Specific Data Functions + */ +int pthread_key_create (pthread_key_t * key, + void (*destructor) (void *)); -int pthread_cond_signal(pthread_cond_t *cv); +int pthread_key_delete (pthread_key_t key); -int pthread_cond_timedwait(pthread_cond_t *cv, - pthread_mutex_t *mutex, - const struct timespec *abstime); +int pthread_setspecific (pthread_key_t key, + const void *value); -int pthread_cond_wait(pthread_cond_t *cv, - pthread_mutex_t *mutex); +void *pthread_getspecific (pthread_key_t key); -int pthread_cond_destroy(pthread_cond_t *cv); -/* Primitives for mutexes. */ +/* + * Mutex Attribute Functions + */ +int pthread_mutexattr_init (pthread_mutexattr_t * attr); -int pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *attr); +int pthread_mutexattr_destroy (pthread_mutexattr_t * attr); -int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutexattr_getpshared (const pthread_mutexattr_t + * attr, + int *pshared); -int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, + int pshared); -int pthread_mutex_trylock(pthread_mutex_t *mutex); +/* + * Mutex Functions + */ +int pthread_mutex_init (pthread_mutex_t * mutex, + const pthread_mutexattr_t * attr); -int pthread_mutex_unlock(pthread_mutex_t *mutex); +int pthread_mutex_destroy (pthread_mutex_t * mutex); -/* Primitives for thread-specific data (TSD). */ +int pthread_mutex_lock (pthread_mutex_t * mutex); -int pthread_key_create(pthread_key_t *key, - void (*destructor)(void *)); +int pthread_mutex_trylock (pthread_mutex_t * mutex); -int pthread_setspecific(pthread_key_t key, void *value); +int pthread_mutex_unlock (pthread_mutex_t * mutex); -void *pthread_getspecific(pthread_key_t key); +/* + * Condition Variable Attribute Functions + */ +int pthread_condattr_init (pthread_condattr_t * attr); -int pthread_key_delete(pthread_key_t key); +int pthread_condattr_destroy (pthread_condattr_t * attr); -/* Signal handling. */ +int pthread_condattr_getpshared (const pthread_condattr_t * attr, + int *pshared); -#if HAVE_SIGSET_T -int pthread_sigmask(int how, - const sigset_t *set, - sigset_t *oset); -#endif /* HAVE_SIGSET_T */ +int pthread_condattr_setpshared (pthread_condattr_t * attr, + int pshared); -/* Thread cancelation functions. */ +/* + * Condition Variable Functions + */ +int pthread_cond_init (pthread_cond_t * cond, + const pthread_condattr_t * attr); -void pthread_testcancel(void); +int pthread_cond_destroy (pthread_cond_t * cond); -int pthread_cancel(pthread_t thread); +int pthread_cond_wait (pthread_cond_t * cond, + pthread_mutex_t * mutex); -#ifdef __cplusplus -} -#endif /* __cplusplus */ +int pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime); -/* Constants declared in global.c */ -extern const int _POSIX_THREAD_THREADS_MAX; -extern const int _POSIX_THREAD_DESTRUCTOR_ITERATIONS; -extern const int _POSIX_THREAD_KEYS_MAX; +int pthread_cond_signal (pthread_cond_t * cond); -extern const int _pthread_create_joinable; -extern const int _pthread_create_detached; +int pthread_cond_broadcast (pthread_cond_t * cond); -extern const int _pthread_cancel_enable; -extern const int _pthread_cancel_disable; +/* + * Scheduling + */ -extern const int _pthread_cancel_asynchronous; -extern const int _pthread_cancel_deferred; +int pthread_setschedparam (pthread_t thread, + int policy, + const struct sched_param *param); -#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS -#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX -#define PTHREAD_THREADS_MAX _POSIX_THREAD_THREADS_MAX +int pthread_getschedparam (pthread_t thread, + int *policy, + struct sched_param *param); -#define PTHREAD_CREATE_JOINABLE _pthread_create_joinable -#define PTHREAD_CREATE_DETACHED _pthread_create_detached +int sched_get_priority_min (int policy); -/* Cancelability attributes */ -#define PTHREAD_CANCEL_ENABLE _pthread_cancel_enable -#define PTHREAD_CANCEL_DISABLE _pthread_cancel_disable +int sched_get_priority_max (int policy); -#define PTHREAD_CANCEL_ASYNCHRONOUS _pthread_cancel_asynchronous -#define PTHREAD_CANCEL_DEFERRED _pthread_cancel_deferred +int pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param); +int pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param); -/* The following #defines implement POSIX cleanup handlers. - The standard requires that these functions be used as statements and - be used pairwise in the same scope. The standard suggests that, in C, they - may be implemented as macros starting and ending the same block. - POSIX requires that applications observe scoping requirements, but - doesn't say if the implemention must enforce them. The macros below - partially enforce scope but can lead to compile or runtime errors. */ +/* + * Protected Methods + * + * This function blocks until the given WIN32 handle + * is signaled or pthread_cancel had been called. + * This function allows the caller to hook into the + * PThreads cancel mechanism. It is implemented using + * + * WaitForMultipleObjects + * + * on 'waitHandle' and a manually reset WIN32 Event + * used to implement pthread_cancel. + */ +int pthreadCancelableWait (HANDLE waitHandle); -enum { - _PTHREAD_HANDLER_POP_LIFO, - _PTHREAD_HANDLER_POP_FIFO -}; +/* + * Thread-Safe C Runtime Library Mappings + * WIN32 C runtime library had been made thread-safe + * without affecting the user interface. Provide + * mappings from the UNIX thread-safe versions to + * the standard C runtime library calls. + * Only provide function mappings for functions that + * actually exist on WIN32. + */ +#define strtok_r( _s, _sep, _lasts ) \ + ( *(_lasts) = strtok( (_s), (_sep) ) ) -enum { - _PTHREAD_CLEANUP_STACK, - _PTHREAD_DESTRUCTOR_STACK, - _PTHREAD_FORKPREPARE_STACK, - _PTHREAD_FORKPARENT_STACK, - _PTHREAD_FORKCHILD_STACK -}; +#define asctime_r( _tm, _buf ) \ + ( strcpy( (_buf), asctime( (_tm) ) ), \ + (_buf) ) -#ifdef pthread_cleanup_push -#undef pthread_cleanup_push -#endif +#define ctime_r( _clock, _buf ) \ + ( strcpy( (_buf), ctime( (_tm) ) ), \ + (_buf) ) -#define pthread_cleanup_push(routine, arg) \ -{ \ - (void ) _pthread_handler_push(_PTHREAD_CLEANUP_STACK, \ - _PTHREAD_HANDLER_POP_LIFO, routine, arg); +#define gmtime_r( _clock, _result ) \ + ( *(_result) = *gmtime( (_clock) ), \ + (_result) ) -#ifdef pthread_cleanup_pop -#undef pthread_cleanup_pop -#endif +#define localtime_r( _clock, _result ) \ + ( *(_result) = *localtime( (_clock) ), \ + (_result) ) -#define pthread_cleanup_pop(execute) \ - _pthread_handler_pop(_PTHREAD_CLEANUP_STACK, execute);\ -} +#define rand_r( _seed ) \ + rand() + + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ -#endif /* _PTHREADS_H */ +#endif /* PTHREAD_H */ diff --git a/sched.c b/sched.c index e3c3ae7..4680b8f 100644 --- a/sched.c +++ b/sched.c @@ -15,7 +15,9 @@ static int is_attr(const pthread_attr_t *attr) { - return (attr == NULL || attr->valid != _PTHREAD_ATTR_VALID) ? 1 : 0; + return (attr == NULL || + *attr == NULL || + (*attr)->valid != _PTHREAD_ATTR_VALID) ? 1 : 0; } int @@ -27,11 +29,12 @@ pthread_attr_setschedparam(pthread_attr_t *attr, return EINVAL; } - attr->priority = param->sched_priority; + (*attr)->priority = param->sched_priority; return 0; } -int pthread_attr_getschedparam(const pthread_attr_t *attr, +int +pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) { if (is_attr(attr) != 0 || param == NULL) @@ -39,7 +42,7 @@ int pthread_attr_getschedparam(const pthread_attr_t *attr, return EINVAL; } - param->sched_priority = attr->priority; + param->sched_priority = (*attr)->priority; return 0; } @@ -47,7 +50,7 @@ int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { /* Validate the thread id. */ - if (_PTHREAD_VALID(thread) < 0) + if (thread == NULL || thread->threadH == 0) { return EINVAL; } @@ -72,7 +75,8 @@ int pthread_setschedparam(pthread_t thread, int policy, } /* This is practically guaranteed to return TRUE. */ - (void) SetThreadPriority(thread->win32handle, param->sched_priority); + (void) SetThreadPriority(thread->threadH, param->sched_priority); + return 0; } @@ -82,7 +86,7 @@ int pthread_getschedparam(pthread_t thread, int *policy, int prio; /* Validate the thread id. */ - if (_PTHREAD_VALID(thread) != 0) + if (thread == NULL || thread->threadH == 0) { return EINVAL; } @@ -97,7 +101,7 @@ int pthread_getschedparam(pthread_t thread, int *policy, *policy = SCHED_OTHER; /* Fill out the sched_param structure. */ - prio = GetThreadPriority(thread->win32handle); + prio = GetThreadPriority(thread->threadH); if (prio == THREAD_PRIORITY_ERROR_RETURN) { return EINVAL; diff --git a/semaphore.c b/semaphore.c new file mode 100644 index 0000000..c163336 --- /dev/null +++ b/semaphore.c @@ -0,0 +1,254 @@ +/* + * ------------------------------------------------------------- + * + * Module: semaphore.c + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * They are supposed to follow the older UNIX convention for + * reporting errors. That is, on failure they are supposed + * to return a value of -1 and store the appropriate error + * number into 'errno'. + * HOWEVER,errno cannot be modified in a multithreaded + * program on WIN32; therefore, the value is returned as + * the function value. + * It is recommended that you compare for zero (0) for success + * instead of -1 for failure when checking the status of + * these functions. + * + * Contents: + * Public Methods Author + * -------------- ------ + * sem_init John E. Bossom Mar 1998 + * sem_destroy John E. Bossom Mar 1998 + * sem_trywait John E. Bossom Mar 1998 + * sem_wait John E. Bossom Mar 1998 + * sem_post John E. Bossom Mar 1998 + * + * Private Methods + * --------------- + * + * ------------------------------------------------------------- + */ +#include +#include +#include +#include +#include + +#include "semaphore.h" + + +int +sem_init (sem_t * sem, int pshared, unsigned int value) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes an unnamed semaphore. the + * initial value of the semaphore is 'value' + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * pshared + * if zero, this semaphore may only be shared between + * threads in the same process. + * if nonzero, the semaphore can be shared between + * processes + * + * value + * initial value of the semaphore counter + * + * DESCRIPTION + * This function initializes an unnamed semaphore. The + * initial value of the semaphore is set to 'value'. + * + * RESULTS + * 0 successfully created semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSPC a required resource has been exhausted, + * ENOSYS semaphores are not supported, + * EPERM the process lacks appropriate privilege + * + * ------------------------------------------------------ + */ +{ + int result = 0; + + if (pshared != 0) + { + /* + * Creating a semaphore that can be shared between + * processes + */ + result = EPERM; + + } + else + { + /* + * NOTE: Taking advantage of the fact that + * sem_t is a simple structure with one entry; + * We don't have to allocate it... + */ + *sem = CreateSemaphore ( + 0, + value, + 0x7FFFFFF, + NULL); + + if (*sem == 0) + { + result = ENOSPC; + } + } + + return (result); + +} /* sem_init */ + + +int +sem_destroy (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function destroys an unnamed semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function destroys an unnamed semaphore. + * + * RESULTS + * 0 successfully destroyed semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EBUSY threads (or processes) are currently + * blocked on 'sem' + * + * ------------------------------------------------------ + */ +{ + return ((sem == NULL) + ? EINVAL + : (CloseHandle (*sem) + ? 0 + : EINVAL)); + +} /* sem_destroy */ + + +int +sem_trywait (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function tries to wait on a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function tries to wait on a semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * this function returns immediately with the error EAGAIN + * + * RESULTS + * 0 successfully destroyed semaphore, + * EAGAIN the semaphore was already locked, + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * + * ------------------------------------------------------ + */ +{ + return ((sem == NULL) + ? EINVAL + : ((WaitForSingleObject (*sem, 0) == WAIT_TIMEOUT) + ? EAGAIN + : 0)); + +} /* sem_trywait */ + + +int +sem_wait (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits on a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function waits on a semaphore. If the + * semaphore value is greater than zero, it decreases + * its value by one. If the semaphore value is zero, then + * the calling thread (or process) is blocked until it can + * successfully decrease the value or until interrupted by + * a signal. + * + * RESULTS + * 0 successfully destroyed semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * EINTR the function was interrupted by a signal, + * EDEADLK a deadlock condition was detected. + * + * ------------------------------------------------------ + */ +{ + + return ((sem == NULL) + ? EINVAL + : pthreadCancelableWait (*sem) + ); + +} /* sem_wait */ + + +int +sem_post (sem_t * sem) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function posts a wakeup to a semaphore. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * DESCRIPTION + * This function posts a wakeup to a semaphore. If there + * are waiting threads (or processes), on is awakened; + * otherwise, the semaphore value is incremented by one. + * + * RESULTS + * 0 successfully destroyed semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSYS semaphores are not supported, + * + * ------------------------------------------------------ + */ +{ + return ((sem == NULL) + ? EINVAL + : (ReleaseSemaphore (*sem, 1, 0) + ? 0 + : EINVAL)); + +} /* sem_post */ diff --git a/semaphore.h b/semaphore.h new file mode 100644 index 0000000..5eeaf3c --- /dev/null +++ b/semaphore.h @@ -0,0 +1,52 @@ +/* + * ------------------------------------------------------------- + * + * Module: semaphore.h + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * They are supposed to follow the older UNIX convention for + * reporting errors. That is, on failure they are supposed + * to return a value of -1 and store the appropriate error + * number into 'errno'. + * HOWEVER,errno cannot be modified in a multithreaded + * program on WIN32; therefore, the value is returned as + * the function value. + * It is recommended that you compare for zero (0) for success + * instead of -1 for failure when checking the status of + * these functions. + * + * ------------------------------------------------------------- + */ +#if !defined( SEMAPHORE_H ) +#define SEMAPHORE_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef HANDLE sem_t; + +int sem_init (sem_t * sem, int pshared, unsigned int value); + +int sem_destroy (sem_t * sem); + +int sem_trywait (sem_t * sem); + +int sem_wait (sem_t * sem); + +int sem_post (sem_t * sem); + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#endif /* !SEMAPHORE_H */ diff --git a/sync.c b/sync.c index 069d10a..57f56b8 100644 --- a/sync.c +++ b/sync.c @@ -6,183 +6,122 @@ * synchronisation. */ -#include - -/* POSIX STANDARD: A thread may pass a value pointer to some data via - pthread_exit(). That pointer will be stored in a location supplied - as an argument to pthread_join(). - - IMPLEMENTATION: The value_ptr is stored in the thread entry. When - pthread_join() wakes up after waiting, or immediately if the target - thread has already terminated but is not detached, the value - pointer from pthread_exit() will be copied to *value_ptr. - - If the target thread does not become detached in the mean time, all - waiting joins on that thread will get the value pointer. The last - waiting join will delete the target thread entry. - - ---- - - POSIX STANDARD: The results of multiple simultaneous calls to - pthread_join() specifying the same target thread are undefined. - - IMPLEMENTATION: Any such join that occurs before the first such - join wakes up, or the thread is otherwise detached (by a call to - pthread_detach), will return successfully with the value that was - passed to pthread_exit(). After the last such join returns, the - target thread will have be detached and it's entry removed from the - thread table. - - Until the target thread entry is deleted it will be counted against - {PTHREAD_COUNT_MAX}. - - ---- - - ---- - - POSIX STANDARD: It is unspecified whether a thread that has exited - but remains unjoined counts against {PTHREAD_COUNT_MAX}. - - IMPLEMENTATION: A thread that has exited but remains unjoined will - be counted against {PTHREAD_COUNT_MAX}. The first call to - pthread_join() or pthread_detach() will remove the target thread's - table entry and decrement the count. - - ---- */ - -#include #include "pthread.h" #include "implement.h" +/* + * Code contributed by John E. Bossom . + */ + int -pthread_join(pthread_t thread, void ** valueptr) +pthread_detach (pthread_t tid) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function detaches the given thread. + * + * PARAMETERS + * thread + * an instance of a pthread_t + * + * + * DESCRIPTION + * This function detaches the given thread. You may + * detach the main thread or to detach a joinable thread + * (You should have used pthread_attr_t to create the + * thread as detached!) + * NOTE: detached threads cannot be joined nor canceled; + * storage is freed immediately on termination. + * + * RESULTS + * 0 successfully detached the thread, + * EINVAL thread is not a joinable thread, + * ENOSPC a required resource has been exhausted, + * ESRCH no thread could be found for 'thread', + * + * ------------------------------------------------------ + */ { - int detachstate; + int result; - /* First check if we are trying to join to ourselves. */ - if (thread == pthread_self()) + if (tid == NULL || + tid->detachState == PTHREAD_CREATE_DETACHED) { - return EDEADLK; - } - - if (thread != NULL) - { - int ret; - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); - - /* If the thread is in DETACHED state, then join will return - immediately. */ - - if (pthread_attr_getdetachstate(&(thread->attr), &detachstate) != 0 - || detachstate == PTHREAD_CREATE_DETACHED) - { - return EINVAL; - } - - thread->join_count++; - - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ - - /* CANCELATION POINT */ - pthread_testcancel(); - /* Wait on the kernel thread object. */ - switch (WaitForSingleObject(thread->win32handle, INFINITE)) - { - case WAIT_FAILED: - /* The thread does not exist. */ - return ESRCH; - case WAIT_OBJECT_0: - /* The thread has finished. */ - break; - default: - /* This should never happen. */ - break; - } - - /* We know the target thread entry still exists at this point - because we incremented join_count above after checking. The - thread entry will not be removed until join_count == 0 again, - ie. when the last waiting join has passed through the - following critical section. */ - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); - - /* Collect the value pointer passed to pthread_exit(). If - another thread detaches our target thread while we're - waiting, then we report a deadlock as it likely that storage - pointed to by thread->joinvalueptr has been freed or - otherwise no longer valid. */ - - if (pthread_attr_getdetachstate(&(thread->attr), &detachstate) != 0 - || detachstate == PTHREAD_CREATE_DETACHED) - { - ret = EDEADLK; - } - else - { - *valueptr = thread->joinvalueptr; - ret = 0; - } - - thread->join_count--; - - /* If we're the last join to return then we are responsible for - removing the target thread's table entry. */ - if (thread->join_count == 0) - { - ret = _pthread_delete_thread(thread); - } + result = EINVAL; - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ - - return ret; } + else + { + result = 0; + tid->detachState = PTHREAD_CREATE_DETACHED; + } + + return (result); - /* Thread not found. */ - return ESRCH; -} +} /* pthread_detach */ int -pthread_detach(pthread_t thread) +pthread_join (pthread_t thread, void **value_ptr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function waits for 'thread' to terminate and + * returns the thread's exit value if 'value_ptr' is not + * NULL. This also detaches the thread on successful + * completion. + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * + * DESCRIPTION + * This function waits for 'thread' to terminate and + * returns the thread's exit value if 'value_ptr' is not + * NULL. This also detaches the thread on successful + * completion. + * NOTE: detached threads cannot be joined or canceled + * + * RESULTS + * 0 'thread' has completed + * EINVAL thread is not a joinable thread, + * ESRCH no thread could be found with ID 'thread', + * EDEADLK attempt to join thread with self + * + * ------------------------------------------------------ + */ { - int detachstate; - int ret; + int result = 0; + pthread_t self; - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_table_mutex); + self = pthread_self (); - if (thread == NULL) + if (pthread_equal (self, thread) == 0) { - ret = ESRCH; + result = EDEADLK; + } else { - /* Check that we can detach this thread. */ - if (pthread_attr_getdetachstate(&(thread->attr), &detachstate) != 0 - || detachstate == PTHREAD_CREATE_DETACHED) + DWORD stat; + + stat = WaitForSingleObject (thread->threadH, INFINITE); + + if (stat != WAIT_OBJECT_0 && + !GetExitCodeThread (thread->threadH, (LPDWORD) value_ptr)) { - ret = EINVAL; + result = ESRCH; } else { - /* This is all we do here - the rest is done either when the - thread exits or when pthread_join() exits. Once this is - set it will never be unset. */ - pthread_attr_setdetachstate(&(thread->attr), - PTHREAD_CREATE_DETACHED); - - ret = 0; + _pthread_threadDestroy (thread); } } - pthread_mutex_unlock(&_pthread_table_mutex); - /* END CRITICAL SECTION */ + return (result); + +} /* pthread_join */ + +/* */ - return ret; -} diff --git a/tests/ChangeLog b/tests/ChangeLog index a634f87..54cccef 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +Wed Dec 30 11:22:44 1998 Ross Johnson + + * tsd1.c: Re-written. See comments at start of file. + * Template.c: New. Contains skeleton code and comment template + intended to fully document the test. + Fri Oct 16 17:59:49 1998 Ross Johnson * tsd1.c (destroy_key): Add function. Change diagnostics. diff --git a/tests/Template.c b/tests/Template.c new file mode 100644 index 0000000..50a81bf --- /dev/null +++ b/tests/Template.c @@ -0,0 +1,68 @@ +/* + * File: + * + * Test Synopsis: + * - + * + * Test Method (Validation or Falsification): + * - + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - + * + * Environment: + * - + * + * Input: + * - + * + * Output: + * - + * + * Assumptions: + * - + * + * Pass Criteria: + * - + * + * Fail Criteria: + * - + */ + +#include +#include + +pthread_key_t key; +pthread_once_t key_once = PTHREAD_ONCE_INIT; + +void * +mythread(void * arg) +{ + return 0; +} + +int +main() +{ + int rc; + pthread_t t1, t2; + + rc = pthread_create(&t1, NULL, mythread, (void *) 1); + printf("pthread_create returned %d\n", rc); + + rc = pthread_create(&t2, NULL, mythread, (void *) 2); + printf("pthread_create returned %d\n", rc); + + /* Give threads time to run. */ + Sleep(2000); + return 0; +} diff --git a/tests/tsd1.c b/tests/tsd1.c index aa2d3da..5236fe7 100644 --- a/tests/tsd1.c +++ b/tests/tsd1.c @@ -1,7 +1,53 @@ +/* + * File: tsd1.c + * + * Test Synopsis: + * - Thread Specific Data (TSD) key creation and destruction. + * + * Description: + * - + * + * Test Method (validation or falsification): + * - validation + * + * Requirements Tested: + * - keys are created for each existing thread including the main thread + * - keys are created for newly created threads + * - keys are thread specific + * - destroy routine is called on each thread exit including the main thread + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Environment: + * - + * + * Input: + * - none + * + * Output: + * - text to stdout + * + * Assumptions: + * - already validated: pthread_create() + * pthread_once() + * - main thread also has a POSIX thread identity + * + * Pass Criteria: + * - stdout matches file reference/tsd1.out + * + * Fail Criteria: + * - fails to match file reference/tsd1.out + * - output identifies failed component + */ + #include #include -pthread_key_t key; +pthread_key_t key = NULL; pthread_once_t key_once = PTHREAD_ONCE_INIT; void @@ -11,9 +57,6 @@ destroy_key(void * arg) printf("SUCCESS: %s: destroying key.\n", (char *) arg); free((char *) arg); - - /* Is it our responsibility to do this? */ - arg = NULL; } void @@ -26,44 +69,50 @@ make_key(void) } } -void * -mythread(void * arg) +void +setkey(void * arg) { void * ptr; - (void) pthread_once(&key_once, make_key); - if ((ptr = pthread_getspecific(key)) != NULL) { - printf("ERROR: Thread %d, Key 0x%x not initialised to NULL\n", - (int) arg, - (int) key); + printf("ERROR: Thread %d, Key not initialised to NULL\n", + (int) arg); exit(1); } else { ptr = (void *) malloc(80); - sprintf((char *) ptr, "Thread %d Key 0x%x", - (int) arg, - (int) key); + sprintf((char *) ptr, "Thread %d Key", + (int) arg); (void) pthread_setspecific(key, ptr); } if ((ptr = pthread_getspecific(key)) == NULL) { - printf("FAILED: Thread %d Key 0x%x: key value set or get failed.\n", - (int) arg, - (int) key); + printf("FAILED: Thread %d Key value set or get failed.\n", + (int) arg); exit(1); } else { - printf("SUCCESS: Thread %d Key 0x%x: key value set and get succeeded.\n", - (int) arg, - (int) key); + printf("SUCCESS: Thread %d Key value set and get succeeded.\n", + (int) arg); printf("SUCCESS: %s: exiting thread.\n", (char *) ptr); } +} + +void * +mythread(void * arg) +{ + while (key == NULL) + { + } + + printf("Thread %d, Key created\n", (int) arg); + + setkey(arg); return 0; @@ -74,14 +123,29 @@ int main() { int rc; - pthread_t t1, t2; - - rc = pthread_create(&t1, NULL, mythread, (void *) 1); - printf("pthread_create returned %d\n", rc); + int t; + pthread_t thread[10]; - rc = pthread_create(&t2, NULL, mythread, (void *) 2); - printf("pthread_create returned %d\n", rc); + for (t = 0; t < 5; t++) + { + rc = pthread_create(&thread[t], NULL, mythread, (void *) (t + 1)); + printf("pthread_create returned %d\n", rc); + } + + (void) pthread_once(&key_once, make_key); + + /* Test main thread key. */ + setkey((void *) 0); + + Sleep(500); + + for (t = 5; t < 10; t++) + { + rc = pthread_create(&thread[t], NULL, mythread, (void *) (t + 1)); + printf("pthread_create returned %d\n", rc); + } Sleep(2000); return 0; } + diff --git a/tsd.c b/tsd.c index cc0b98e..72a03ea 100644 --- a/tsd.c +++ b/tsd.c @@ -5,202 +5,306 @@ * POSIX thread functions which implement thread-specific data (TSD). */ -/* - * Why we can't use Win32 TLS - * -------------------------- - * - * In a word: Destructors - * - * POSIX 1003.1 1996, Section 17 allows for optional destructor functions - * to be associated with each key value. - * - * This is my (revised) understanding of how destructors work: - * - * A key is created by a single thread, which then provides in every - * existing thread a TSD matching the same key, but initialised - * to NULL. Each new thread will also get a matching key with value NULL. - * The creating thread can optionally associate a function, called a - * destructor, with the key. - * - * When each thread exits, it calls the destructor function, which - * will then perform an action on that threads key value - * only. (Previously I thought that only the key creating thread ran - * the destructor on the key in all threads. That proposition is - * sounding scarier by the minute.) - * - * SOME APPROACHES TO MANAGING TSD MEMORY - * - * We could simply allocate enough memory on process startup to hold - * all possible data for all possible threads. - * - * We could allocate memory for just a table to hold a single pointer - * for each of POSIX_THREAD_KEYS_MAX keys. pthread_key_create() could then - * allocate space for POSIX_THREADS_MAX key values in one hit and store - * the location of the array in the first table. - * - * The standard also suggests that each thread might store key/value pairs - * on its private stack. This seems like a good idea. I had concerns about - * memory leaks and key re-use if a key was deleted, but the standard talks - * at length on this and basically says it's up to the application to - * make sure everything goes smoothly here, making sure that proper cleanup - * is done before a key is deleted. (section B.17.1.3 in particular) - * - * One more thing to note: destructors must never be called on deleted keys. - */ - -#include - #include "pthread.h" #include "implement.h" +/* + * Code contributed by John E. Bossom . + */ + int -pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) +pthread_key_create (pthread_key_t * key, void (*destructor) (void *)) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function creates a thread-specific data key visible + * to all threads. All existing and new threads have a value + * NULL for key until set using pthread_setspecific. When any + * thread with a non-NULL value for key terminates, 'destructor' + * is called with key's current value for that thread. + * + * PARAMETERS + * key + * pointer to an instance of pthread_key_t + * + * + * DESCRIPTION + * This function creates a thread-specific data key visible + * to all threads. All existing and new threads have a value + * NULL for key until set using pthread_setspecific. When any + * thread with a non-NULL value for key terminates, 'destructor' + * is called with key's current value for that thread. + * + * RESULTS + * 0 successfully created semaphore, + * EAGAIN insufficient resources or PTHREAD_KEYS_MAX + * exceeded, + * ENOMEM insufficient memory to create the key, + * + * ------------------------------------------------------ + */ { - pthread_key_t k; - int ret = 0; - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); - - if (_pthread_key_reuse_top >= 0) + int result = 0; + + if ((*key = (pthread_key_t) calloc (1, sizeof (**key))) == NULL) { - k = _pthread_key_reuse[_pthread_key_reuse_top--]; + result = ENOMEM; } - else + else if (((*key)->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES) { - if (_pthread_key_virgin_next < PTHREAD_KEYS_MAX) - { - k = _pthread_key_virgins[_pthread_key_virgin_next++]; - } - else - { - return EAGAIN; - } - } - - /* FIXME: This needs to be implemented as a list plus a re-use stack as for - thread IDs. _pthread_destructor_run_all() then needs to be changed - to push keys onto the re-use stack. - */ - - _pthread_tsd_key_table[k].in_use = 0; - _pthread_tsd_key_table[k].status = _PTHREAD_TSD_KEY_INUSE; - _pthread_tsd_key_table[k].destructor = destructor; - - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - - *key = k; - - return ret; -} - -int -pthread_setspecific(pthread_key_t key, void *value) -{ - void ** keys; - int inuse; - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); - - inuse = (_pthread_tsd_key_table[key].status == _PTHREAD_TSD_KEY_INUSE); - - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - - if (! inuse) - return EINVAL; - - keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex); + /* + * Create system key + */ + result = EAGAIN; - if (keys[key] != NULL) - { - if (value == NULL) - { - /* Key is no longer in use by this thread. */ - _pthread_tsd_key_table[key].in_use--; - } + free (*key); + *key = NULL; } - else + else if (destructor != NULL) { - if (value != NULL) - { - /* Key is now in use by this thread. */ - _pthread_tsd_key_table[key].in_use++; - } + /* + * Have to manage associations between thread and key; + * Therefore, need a lock that allows multiple threads + * to gain exclusive access to the key->threads list + */ + result = pthread_mutex_init (&((*key)->threadsLock), NULL); + + if (result != 0) + { + TlsFree ((*key)->key); + + free (*key); + *key = NULL; + } + (*key)->destructor = destructor; } - keys[key] = value; - - return 0; + return (result); } -void * -pthread_getspecific(pthread_key_t key) +int +pthread_key_delete (pthread_key_t key) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function deletes a thread-specific data key. This + * does not change the value of the thread spcific data key + * for any thread and does not run the key's destructor + * in any thread so it should be used with caution. + * + * PARAMETERS + * key + * pointer to an instance of pthread_key_t + * + * + * DESCRIPTION + * This function deletes a thread-specific data key. This + * does not change the value of the thread spcific data key + * for any thread and does not run the key's destructor + * in any thread so it should be used with caution. + * + * RESULTS + * 0 successfully deleted the key, + * EINVAL key is invalid, + * + * ------------------------------------------------------ + */ { - void ** keys; - int inuse; - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); - - inuse = (_pthread_tsd_key_table[key].status == _PTHREAD_TSD_KEY_INUSE); + int result = 0; - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - - if (! inuse) - return (void *) NULL; + if (key != NULL) + { + if (key->threads != NULL && + pthread_mutex_lock (&(key->threadsLock)) == 0) + { + /* + * Run through all Thread<-->Key associations + * for this key. + * If the pthread_t still exits (ie the assoc->thread + * is not NULL) then leave the assoc for the thread to + * destroy. + * Notes: + * If assoc->thread is NULL, then the associated thread + * is no longer referencing this assoc. + * The association is only referenced + * by this key and must be released; otherwise + * the assoc will be destroyed when the thread is destroyed. + */ + ThreadKeyAssoc *assoc; + + assoc = (ThreadKeyAssoc *) key->threads; + + while (assoc != NULL) + { + if (pthread_mutex_lock (&(assoc->lock)) == 0) + { + ThreadKeyAssoc *next; + + assoc->key = NULL; + next = assoc->nextThread; + assoc->nextThread = NULL; + + pthread_mutex_unlock (&(assoc->lock)); + + _pthread_tkAssocDestroy (assoc); + + assoc = next; + } + } + pthread_mutex_unlock (&(key->threadsLock)); + } + + TlsFree (key->key); + if (key->destructor != NULL) + { + pthread_mutex_destroy (&(key->threadsLock)); + } + +#if defined( _DEBUG ) + memset ((char *) key, 0, sizeof (*key)); +#endif + free (key); + } - keys = (void **) TlsGetValue(_pthread_TSD_keys_TlsIndex); - return keys[key]; + return (result); } -/* - pthread_key_delete: - - ANSI/IEEE Std 1003.1, 1996 Edition - - Section 17.1.3.2 - - This function deletes a thread-specific data key previously returned by - pthread_key_create(). The thread specific data values associated with - "key" need not be NULL at the time pthread_key_delete() is called. It is - the responsibility of the application to free any application storage - or perform any cleanup actions for data structures related to the deleted - key or associated thread-specific data in any threads; this cleanup - can be done either before or after pthread_key_delete() is called. Any - attempt to use "key" following the call to pthread_key_delete() - results in undefined behaviour. - - The pthread_key_delete() function shall be callable from within - destructor functions. No destructor functions shall be invoked by - pthread_key_delete(). Any destructor function that may have been associated - with "key" shall no longer be called upon thread exit. - */ int -pthread_key_delete(pthread_key_t key) +pthread_setspecific (pthread_key_t key, const void *value) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function initializes an unnamed semaphore. the + * initial value of the semaphore is 'value' + * + * PARAMETERS + * sem + * pointer to an instance of sem_t + * + * + * DESCRIPTION + * This function initializes an unnamed semaphore. The + * initial value of the semaphore is set to 'value'. + * + * RESULTS + * 0 successfully created semaphore, + * EINVAL 'sem' is not a valid semaphore, + * ENOSPC a required resource has been exhausted, + * ENOSYS semaphores are not supported, + * EPERM the process lacks appropriate privilege + * + * ------------------------------------------------------ + */ { - int ret = 0; - - /* CRITICAL SECTION */ - pthread_mutex_lock(&_pthread_tsd_mutex); + pthread_t self; + int result = 0; - if (_pthread_tsd_key_table[key].status != _PTHREAD_TSD_KEY_INUSE) + if (key != _pthread_selfThreadKey) { - ret = EINVAL; + /* + * Using pthread_self will implicitly create + * an instance of pthread_t for the current + * thread if one wasn't explicitly created + */ + self = pthread_self (); } else { - _pthread_tsd_key_table[key].status = _PTHREAD_TSD_KEY_DELETED; - _pthread_tsd_key_table[key].destructor = NULL; + /* + * Resolve catch-22 of registering thread with threadSelf + * key + */ + self = pthread_getspecific (_pthread_selfThreadKey); + if (self == NULL) + { + self = (pthread_t) value; + } + } + + result = 0; + + if (key != NULL) + { + ThreadKeyAssoc *assoc; + + if (self != NULL && + key->destructor != NULL && + value != NULL) + { + /* + * Only require associations if we have to + * call user destroy routine. + * Don't need to locate an existing association + * when setting data to NULL for WIN32 since the + * data is stored with the operating system; not + * on the association; setting assoc to NULL short + * circuits the search. + */ + assoc = (ThreadKeyAssoc *) self->keys; + /* + * Locate existing association + */ + while (assoc != NULL) + { + if (assoc->key == key) + { + /* + * Association already exists + */ + break; + } + assoc = assoc->nextKey; + } + + /* + * create an association if not found + */ + result = (assoc == NULL) + ? _pthread_tkAssocCreate (&assoc, self, key) + : 0; + } + else + { + result = 0; + } + + if (result == 0) + { + TlsSetValue (key->key, (LPVOID) value); + } } + return (result); +} /* pthread_setspecific */ - pthread_mutex_unlock(&_pthread_tsd_mutex); - /* END CRITICAL SECTION */ - return ret; +void * +pthread_getspecific (pthread_key_t key) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * This function returns the current value of key in the + * calling thread. If no value has been set for 'key' in + * the thread, NULL is returned. + * + * PARAMETERS + * key + * an instance of pthread_key_t + * + * + * DESCRIPTION + * This function returns the current value of key in the + * calling thread. If no value has been set for 'key' in + * the thread, NULL is returned. + * + * RESULTS + * key value + * + * ------------------------------------------------------ + */ +{ + return (TlsGetValue (key->key)); } +/* */ + -- cgit v1.2.3