diff options
| -rw-r--r-- | ChangeLog | 150 | ||||
| -rw-r--r-- | MAINTAINERS | 2 | ||||
| -rw-r--r-- | attr.c | 350 | ||||
| -rw-r--r-- | buildlib.bat | 3 | ||||
| -rw-r--r-- | cancel.c | 246 | ||||
| -rw-r--r-- | cleanup.c | 306 | ||||
| -rw-r--r-- | condvar.c | 762 | ||||
| -rw-r--r-- | create.c | 231 | ||||
| -rw-r--r-- | dll.c | 119 | ||||
| -rw-r--r-- | exit.c | 87 | ||||
| -rw-r--r-- | fork.c | 6 | ||||
| -rw-r--r-- | global.c | 72 | ||||
| -rw-r--r-- | implement.h | 292 | ||||
| -rw-r--r-- | misc.c | 211 | ||||
| -rw-r--r-- | private.c | 435 | ||||
| -rw-r--r-- | pthread.def | 4 | ||||
| -rw-r--r-- | pthread.h | 1041 | ||||
| -rw-r--r-- | sched.c | 20 | ||||
| -rw-r--r-- | semaphore.c | 254 | ||||
| -rw-r--r-- | semaphore.h | 52 | ||||
| -rw-r--r-- | sync.c | 243 | ||||
| -rw-r--r-- | tests/ChangeLog | 6 | ||||
| -rw-r--r-- | tests/Template.c | 68 | ||||
| -rw-r--r-- | tests/tsd1.c | 116 | ||||
| -rw-r--r-- | tsd.c | 436 | 
25 files changed, 4006 insertions, 1506 deletions
| @@ -2,18 +2,166 @@  	* README: Update info about subscribing to the mailing list. +Mon Jan  4 11:23:40 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@ixobrychus.canberra.edu.au> +	* 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  <rpj@ixobrychus.canberra.edu.au>  	* 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 @@ -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 + + @@ -5,80 +5,238 @@   * POSIX thread functions related to thread cancellation.   */ -#include <errno.h> -  #include "pthread.h"  #include "implement.h" +/* + * Code contributed by John E. Bossom <JEB>. + */ +  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);  } + +/* </JEB> */ + @@ -6,211 +6,139 @@   * threads.   */ -#include <errno.h> -  #include <malloc.h> +  #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 <JEB>. + */ -  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; +/* </JEB> */ -      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; -    } -} @@ -5,200 +5,702 @@   * This translation unit implements condition variables and their primitives.   */ -#include <errno.h> +/* + * Code contributed by John E. Bossom <JEB>. + */ + +#include <string.h> -#include <windows.h>  #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);  } + +/* </JEB> */ + @@ -6,145 +6,142 @@   * thread.   */ -#include <errno.h> - -#include <windows.h> -#include <process.h> -#include <string.h> -  #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 <JEB>. + */ -  /* 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 */ +/* </JEB> */ @@ -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 <windows.h>  #include <malloc.h>  #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 */ @@ -6,69 +6,44 @@   * a thread.   */ -#include <windows.h> -#include <process.h>  #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 <JEB>. + */ -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(); +/* </JEB> */ -  /* 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); -} @@ -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 */ @@ -6,78 +6,12 @@   * as a whole.   */ -#include <windows.h> -#include <process.h>  #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 <JEB>. + */ -/* 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; +/* </JEB> */  #endif /* _IMPLEMENT_H */ @@ -8,6 +8,7 @@  #include <errno.h>  #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 <JEB>. + */ +  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 */ + + +/* </JEB> */ + @@ -11,133 +11,392 @@  #include "pthread.h"  #include "implement.h" -/* Thread ID management. -   --------------------- +/* + * Code contributed by John E. Bossom <JEB>. + */ -   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 */ + +/* </JEB> */ + 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 @@ -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 <process.h> +#include <errno.h> -#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 <semaphore.h> +/* #include <sched.h> /**/ -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 */ @@ -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 <pthread.h> +#include <windows.h> +#include <process.h> +#include <errno.h> +#include <string.h> + +#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 <process.h> +#include <errno.h> + +#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 */ @@ -6,183 +6,122 @@   * synchronisation.   */ -#include <errno.h> - -/* 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 <windows.h>  #include "pthread.h"  #include "implement.h" +/* + * Code contributed by John E. Bossom <JEB>. + */ +  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 */ + +/* </JEB> */ -  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  <rpj@ixobrychus.canberra.edu.au> + +	* 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  <rpj@swan.canberra.edu.au>  	* 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 <pthread.h> +#include <stdio.h> + +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 <pthread.h>  #include <stdio.h> -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;  } + @@ -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 <errno.h> -  #include "pthread.h"  #include "implement.h" +/* + * Code contributed by John E. Bossom <JEB>. + */ +  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));  } +/* </JEB> */ + | 
