summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog150
-rw-r--r--MAINTAINERS2
-rw-r--r--attr.c350
-rw-r--r--buildlib.bat3
-rw-r--r--cancel.c246
-rw-r--r--cleanup.c306
-rw-r--r--condvar.c762
-rw-r--r--create.c231
-rw-r--r--dll.c119
-rw-r--r--exit.c87
-rw-r--r--fork.c6
-rw-r--r--global.c72
-rw-r--r--implement.h292
-rw-r--r--misc.c211
-rw-r--r--private.c435
-rw-r--r--pthread.def4
-rw-r--r--pthread.h1041
-rw-r--r--sched.c20
-rw-r--r--semaphore.c254
-rw-r--r--semaphore.h52
-rw-r--r--sync.c243
-rw-r--r--tests/ChangeLog6
-rw-r--r--tests/Template.c68
-rw-r--r--tests/tsd1.c116
-rw-r--r--tsd.c436
25 files changed, 4006 insertions, 1506 deletions
diff --git a/ChangeLog b/ChangeLog
index 04b46ac..ae2d20d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,18 +2,166 @@
* README: Update info about subscribing to the mailing list.
+Mon Jan 4 11:23:40 1999 Ross Johnson <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
diff --git a/attr.c b/attr.c
index 418c3e8..3ec1d12 100644
--- a/attr.c
+++ b/attr.c
@@ -16,15 +16,57 @@ is_attr(const pthread_attr_t *attr)
{
/* Return 0 if the attr object is valid, non-zero otherwise. */
- return (attr == NULL || attr->valid != _PTHREAD_ATTR_VALID);
+ return (attr == NULL ||
+ *attr == NULL ||
+ (*attr)->valid != _PTHREAD_ATTR_VALID);
}
-#ifdef _POSIX_THREAD_ATTR_STACKSIZE
int
pthread_attr_setstacksize(pthread_attr_t *attr,
size_t stacksize)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function specifies the size of the stack on
+ * which threads created with 'attr' will run.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * stacksize
+ * stack size, in bytes.
+ *
+ *
+ * DESCRIPTION
+ * This function specifies the size of the stack on
+ * which threads created with 'attr' will run.
+ *
+ * NOTES:
+ * 1) Function supported only if this macro is
+ * defined:
+ *
+ * _POSIX_THREAD_ATTR_STACKSIZE
+ *
+ * 2) Find the default first (using
+ * pthread_attr_getstacksize), then increase
+ * by multiplying.
+ *
+ * 3) Only use if thread needs more than the
+ * default.
+ *
+ * RESULTS
+ * 0 successfully set stack size,
+ * EINVAL 'attr' is invalid or stacksize too
+ * small or too big.
+ * ENOSYS function not supported
+ *
+ * ------------------------------------------------------
+ */
{
+#ifdef _POSIX_THREAD_ATTR_STACKSIZE
+
/* Verify that the stack size is within range. */
if (stacksize < PTHREAD_STACK_MIN)
{
@@ -37,89 +79,295 @@ pthread_attr_setstacksize(pthread_attr_t *attr,
}
/* Everything is okay. */
- attr->stacksize = stacksize;
+ (*attr)->stacksize = stacksize;
return 0;
+
+#else
+
+ return ENOSYS;
+
+#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
+
}
int
pthread_attr_getstacksize(const pthread_attr_t *attr,
size_t *stacksize)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function determines the size of the stack on
+ * which threads created with 'attr' will run.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * stacksize
+ * pointer to size_t into which is returned the
+ * stack size, in bytes.
+ *
+ *
+ * DESCRIPTION
+ * This function determines the size of the stack on
+ * which threads created with 'attr' will run.
+ *
+ * NOTES:
+ * 1) Function supported only if this macro is
+ * defined:
+ *
+ * _POSIX_THREAD_ATTR_STACKSIZE
+ *
+ * 2) Use on newly created attributes object to
+ * find the default stack size.
+ *
+ * RESULTS
+ * 0 successfully retrieved stack size,
+ * EINVAL 'attr' is invalid
+ * ENOSYS function not supported
+ *
+ * ------------------------------------------------------
+ */
{
+#ifdef _POSIX_THREAD_ATTR_STACKSIZE
+
if (is_attr(attr) != 0)
{
return EINVAL;
}
/* Everything is okay. */
- *stacksize = attr->stacksize;
+ *stacksize = (*attr)->stacksize;
return 0;
-}
+
+#else
+
+ return ENOSYS;
#endif /* _POSIX_THREAD_ATTR_STACKSIZE */
-#ifdef _POSIX_THREAD_ATTR_STACKADDR
+}
+
int
pthread_attr_setstackaddr(pthread_attr_t *attr,
void *stackaddr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Threads created with 'attr' will run on the stack
+ * starting at 'stackaddr'.
+ * Stack must be at least PTHREAD_STACK_MIN bytes.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * stacksize
+ * stack size, in bytes.
+ *
+ *
+ * DESCRIPTION
+ * Threads created with 'attr' will run on the stack
+ * starting at 'stackaddr'.
+ * Stack must be at least PTHREAD_STACK_MIN bytes.
+ *
+ * NOTES:
+ * 1) Function supported only if this macro is
+ * defined:
+ *
+ * _POSIX_THREAD_ATTR_STACKADDR
+ *
+ * 2) Create only one thread for each stack
+ * address..
+ *
+ * 3) Ensure that stackaddr is aligned.
+ *
+ * RESULTS
+ * 0 successfully set stack address,
+ * EINVAL 'attr' is invalid
+ * ENOSYS function not supported
+ *
+ * ------------------------------------------------------
+ */
{
+#if defined( _POSIX_THREAD_ATTR_STACKADDR )
+
if (is_attr(attr) != 0)
{
return EINVAL;
}
+
+ (*attr)->stackaddr = stackaddr;
+ return 0;
+
+#else
+
return ENOSYS;
+
+#endif /* _POSIX_THREAD_ATTR_STACKADDR */
}
int
pthread_attr_getstackaddr(const pthread_attr_t *attr,
void **stackaddr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function determines the address of the stack
+ * on which threads created with 'attr' will run.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * stackaddr
+ * pointer into which is returned the stack address.
+ *
+ *
+ * DESCRIPTION
+ * This function determines the address of the stack
+ * on which threads created with 'attr' will run.
+ *
+ * NOTES:
+ * 1) Function supported only if this macro is
+ * defined:
+ *
+ * _POSIX_THREAD_ATTR_STACKADDR
+ *
+ * 2) Create only one thread for each stack
+ * address..
+ *
+ * RESULTS
+ * 0 successfully retreived stack address,
+ * EINVAL 'attr' is invalid
+ * ENOSYS function not supported
+ *
+ * ------------------------------------------------------
+ */
{
+#if defined( _POSIX_THREAD_ATTR_STACKADDR )
+
if (is_attr(attr) != 0)
{
return EINVAL;
}
+
+ *stackaddr = (*attr)->stackaddr;
+ return 0;
+
+#else
+
return ENOSYS;
-}
#endif /* _POSIX_THREAD_ATTR_STACKADDR */
+}
int
pthread_attr_init(pthread_attr_t *attr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Initializes a thread attributes object with default
+ * attributes.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ *
+ * DESCRIPTION
+ * Initializes a thread attributes object with default
+ * attributes.
+ *
+ * NOTES:
+ * 1) Used to define thread attributes
+ *
+ * RESULTS
+ * 0 successfully initialized attr,
+ * ENOMEM insufficient memory for attr.
+ *
+ * ------------------------------------------------------
+ */
{
+ pthread_attr_t attr_result;
+ int result = 0;
+
if (attr == NULL)
{
/* This is disallowed. */
return EINVAL;
}
+ attr_result = malloc (sizeof (*attr_result));
+
+ if (attr_result == NULL)
+ {
+ return ENOMEM;
+ }
+
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
- attr->stacksize = PTHREAD_STACK_MIN;
+ attr_result->stacksize = PTHREAD_STACK_MIN;
#endif
- attr->detachedstate = PTHREAD_CREATE_JOINABLE;
+#ifdef _POSIX_THREAD_ATTR_STACKADDR
+ /* FIXME: Set this to something sensible when we support it. */
+ attr_result->stackaddr = NULL;
+#endif
+
+ attr_result->detachstate = PTHREAD_CREATE_JOINABLE;
+
#if HAVE_SIGSET_T
- memset(&(attr->sigmask), 0, sizeof(sigset_t));
+ memset(&(attr_result->sigmask), 0, sizeof(sigset_t));
#endif /* HAVE_SIGSET_T */
/* Priority uses Win32 priority values. */
- attr->priority = THREAD_PRIORITY_NORMAL;
+ attr_result->priority = THREAD_PRIORITY_NORMAL;
+
+ attr_result->valid = _PTHREAD_ATTR_VALID;
- attr->valid = _PTHREAD_ATTR_VALID;
+ *attr = attr_result;
return 0;
}
int
pthread_attr_destroy(pthread_attr_t *attr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Destroys a thread attributes object.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ *
+ * DESCRIPTION
+ * Destroys a thread attributes object.
+ *
+ * NOTES:
+ * 1) Does not affect threads created with 'attr'.
+ *
+ * RESULTS
+ * 0 successfully destroyed attr,
+ * EINVAL 'attr' is invalid.
+ *
+ * ------------------------------------------------------
+ */
{
if (is_attr(attr) != 0)
{
return EINVAL;
}
- /* Set the attribute object to a specific invalid value. */
- attr->valid = 0;
+ /*
+ * Set the attribute object to a specific invalid value.
+ */
+ (*attr)->valid = 0;
+ free (*attr);
+ *attr = NULL;
return 0;
}
@@ -127,19 +375,89 @@ pthread_attr_destroy(pthread_attr_t *attr)
int
pthread_attr_getdetachstate(const pthread_attr_t *attr,
int *detachstate)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function determines whether threads created with
+ * 'attr' will run detached.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * detachstate
+ * pointer to an integer into which is returned one
+ * of:
+ *
+ * PTHREAD_CREATE_JOINABLE
+ * Thread ID is valid, must be joined
+ *
+ * PTHREAD_CREATE_DETACHED
+ * Thread ID is invalid, cannot be joined,
+ * canceled, or modified
+ *
+ *
+ * DESCRIPTION
+ * This function determines whether threads created with
+ * 'attr' will run detached.
+ *
+ * NOTES:
+ * 1) You cannot join or cancel detached threads.
+ *
+ * RESULTS
+ * 0 successfully retrieved detach state,
+ * EINVAL 'attr' is invalid
+ *
+ * ------------------------------------------------------
+ */
{
if (is_attr(attr) != 0 || detachstate == NULL)
{
+ *detachstate = PTHREAD_CREATE_DETACHED;
return EINVAL;
}
- *detachstate = attr->detachedstate;
+ *detachstate = (*attr)->detachstate;
return 0;
}
int
pthread_attr_setdetachstate(pthread_attr_t *attr,
int detachstate)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * This function specifies whether threads created with
+ * 'attr' will run detached.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_attr_t
+ *
+ * detachstate
+ * an integer containing one of:
+ *
+ * PTHREAD_CREATE_JOINABLE
+ * Thread ID is valid, must be joined
+ *
+ * PTHREAD_CREATE_DETACHED
+ * Thread ID is invalid, cannot be joined,
+ * canceled, or modified
+ *
+ *
+ * DESCRIPTION
+ * This function specifies whether threads created with
+ * 'attr' will run detached.
+ *
+ * NOTES:
+ * 1) You cannot join or cancel detached threads.
+ *
+ * RESULTS
+ * 0 successfully set detach state,
+ * EINVAL 'attr' or 'detachstate' is invalid
+ *
+ * ------------------------------------------------------
+ */
{
if (is_attr(attr) != 0)
{
@@ -152,6 +470,6 @@ pthread_attr_setdetachstate(pthread_attr_t *attr,
return EINVAL;
}
- attr->detachedstate = detachstate;
+ (*attr)->detachstate = detachstate;
return 0;
}
diff --git a/buildlib.bat b/buildlib.bat
index e8a674e..5e2e1c8 100644
--- a/buildlib.bat
+++ b/buildlib.bat
@@ -16,5 +16,8 @@ cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c sched.
cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c signal.c
cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c sync.c
cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c tsd.c
+cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c semaphore.c
cl /LD /Zi *.obj /Fepthread.dll /link /nodefaultlib:libcmt /implib:pthread.lib msvcrt.lib /def:pthread.def
+
+
diff --git a/cancel.c b/cancel.c
index 9dd99c6..93d6e07 100644
--- a/cancel.c
+++ b/cancel.c
@@ -5,80 +5,238 @@
* POSIX thread functions related to thread cancellation.
*/
-#include <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> */
+
diff --git a/cleanup.c b/cleanup.c
index 0a5ae3f..12ce282 100644
--- a/cleanup.c
+++ b/cleanup.c
@@ -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;
- }
-}
diff --git a/condvar.c b/condvar.c
index 3dc76d4..6304391 100644
--- a/condvar.c
+++ b/condvar.c
@@ -5,200 +5,702 @@
* This translation unit implements condition variables and their primitives.
*/
-#include <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> */
+
diff --git a/create.c b/create.c
index 6119198..37e1c61 100644
--- a/create.c
+++ b/create.c
@@ -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> */
diff --git a/dll.c b/dll.c
index bb3d7ef..dc1788f 100644
--- a/dll.c
+++ b/dll.c
@@ -5,26 +5,10 @@
* This translation unit implements DLL initialisation.
*/
-/* We use the DLL entry point function to set up per thread storage
- specifically to hold the threads own thread ID.
-
- The thread ID is stored by _pthread_start_call().
-
- The thread ID is retrieved by pthread_self().
-
- */
-
-#include <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 */
diff --git a/exit.c b/exit.c
index 3472dca..5177e88 100644
--- a/exit.c
+++ b/exit.c
@@ -6,69 +6,44 @@
* a thread.
*/
-#include <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);
-}
diff --git a/fork.c b/fork.c
index 3a25c48..61da545 100644
--- a/fork.c
+++ b/fork.c
@@ -5,12 +5,12 @@
* Implementation of fork() for POSIX threads.
*/
-/* FIXME! */
-#define ENOMEM 0
#include "pthread.h"
#include "implement.h"
+#if 0 /* Pre Bossom */
+
int
pthread_atfork(void (*prepare)(void),
void (*parent)(void),
@@ -139,3 +139,5 @@ fork()
}
#endif /* HAVE_PID_T && HAVE_FORK */
+
+#endif /* Pre Bossom */
diff --git a/global.c b/global.c
index a2ca988..56a26f3 100644
--- a/global.c
+++ b/global.c
@@ -6,78 +6,12 @@
* as a whole.
*/
-#include <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 */
diff --git a/misc.c b/misc.c
index 659a4eb..f49cd51 100644
--- a/misc.c
+++ b/misc.c
@@ -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> */
+
diff --git a/private.c b/private.c
index 32116ef..901973a 100644
--- a/private.c
+++ b/private.c
@@ -11,133 +11,392 @@
#include "pthread.h"
#include "implement.h"
-/* Thread ID management.
- ---------------------
+/*
+ * Code contributed by John E. Bossom <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
diff --git a/pthread.h b/pthread.h
index ef22d80..702028f 100644
--- a/pthread.h
+++ b/pthread.h
@@ -1,33 +1,199 @@
/* This is the POSIX thread API (POSIX 1003).
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public
-License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-/* FIXME: do not include function prototypes for functions which are
- not yet implemented. This will allow us to keep a better handle on
- where we're at. */
-
-#ifndef _PTHREADS_H
-#define _PTHREADS_H
-
-/* Convert these to defined when implemented. */
-#define _POSIX_THREAD_ATTR_STACKSIZE
-#ifdef _POSIX_THREAD_ATTR_STACKADDR
-#undef _POSIX_THREAD_ATTR_STACKADDR
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * -------------------------------------------------------------
+ *
+ *
+ * Module: pthread.h
+ *
+ * Purpose:
+ * Provides an implementation of PThreads based upon the
+ * standard:
+ *
+ * POSIX 1003.1c-1995 (POSIX.1c)
+ *
+ * Authors:
+ * Contributors are listed in the file "MAINTAINERS".
+ *
+ * The following functions are implemented:
+ * ---------------------------
+ * PThreads
+ * ---------------------------
+ * pthread_attr_init
+ * pthread_attr_destroy
+ * pthread_attr_getdetachstate
+ * pthread_attr_getstackaddr
+ * pthread_attr_getstacksize
+ * pthread_attr_setdetachstate
+ * pthread_attr_setstackaddr
+ * pthread_attr_setstacksize
+ *
+ * pthread_create
+ * pthread_detach
+ * pthread_equal
+ * pthread_exit
+ * pthread_join
+ * pthread_self
+ * sched_yield
+ *
+ * pthread_cancel
+ * pthread_cleanup_pop
+ * pthread_cleanup_push
+ * pthread_setcancelstate
+ * pthread_setcanceltype
+ * pthread_testcancel
+ *
+ * ---------------------------
+ * Thread Specific Data
+ * ---------------------------
+ * pthread_key_create
+ * pthread_key_delete
+ * pthread_setspecific
+ * pthread_getspecific
+ *
+ * ---------------------------
+ * Mutexes
+ * ---------------------------
+ * pthread_mutexattr_init
+ * pthread_mutexattr_destroy
+ * pthread_mutexattr_getpshared
+ * pthread_mutexattr_setpshared
+ *
+ * pthread_mutex_init
+ * pthread_mutex_destroy
+ * pthread_mutex_lock
+ * pthread_mutex_trylock
+ * pthread_mutex_unlock
+ *
+ * ---------------------------
+ * Condition Variables
+ * ---------------------------
+ * pthread_condattr_init
+ * pthread_condattr_destroy
+ * pthread_condattr_getpshared
+ * pthread_condattr_setpshared
+ *
+ * pthread_cond_init
+ * pthread_cond_destroy
+ * pthread_cond_wait
+ * pthread_cond_timedwait
+ * pthread_cond_signal
+ * pthread_cond_broadcast
+ *
+ * ---------------------------
+ * Protected Methods
+ * ---------------------------
+ * pthreadCancelableWait
+ *
+ * ---------------------------
+ * RealTime Scheduling:
+ * ---------------------------
+ * pthread_attr_getschedparam
+ * pthread_attr_setschedparam
+ * pthread_getschedparam
+ * pthread_setschedparam
+ * sched_get_priority_max
+ * sched_get_priority_min
+ *
+ * ---------------------------
+ * Signals:
+ * ---------------------------
+ * pthread_sigmask
+ *
+ * Limitations
+ * ===========
+ * The following functions are not implemented:
+ *
+ * ---------------------------
+ * RealTime Scheduling:
+ * ---------------------------
+ * pthread_attr_getinheritsched
+ * pthread_attr_getschedpolicy
+ * pthread_attr_getscope
+ * pthread_attr_setinheritsched
+ * pthread_attr_setschedpolicy
+ * pthread_attr_setscope
+ * pthread_mutex_getprioceiling
+ * pthread_mutex_setprioceiling
+ * pthread_mutex_attr_getprioceiling
+ * pthread_mutex_attr_getprotocol
+ * pthread_mutex_attr_setprioceiling
+ * pthread_mutex_attr_setprotocol
+ *
+ * ---------------------------
+ * Fork Handlers:
+ * ---------------------------
+ * pthread_atfork
+ *
+ * ---------------------------
+ * Stdio:
+ * ---------------------------
+ * flockfile
+ * ftrylockfile
+ * funlockfile
+ * getc_unlocked
+ * getchar_unlocked
+ * putc_unlocked
+ * putchar_unlocked
+ *
+ * ---------------------------
+ * Thread-Safe C Runtime Library:
+ * ---------------------------
+ * readdir_r
+ * getgrgid_r
+ * getgrnam_r
+ * getpwuid_r
+ * getpwnam_r
+ *
+ * ---------------------------
+ * Signals:
+ * ---------------------------
+ * pthread_kill
+ * sigtimedwait
+ * sigwait
+ * sigwaitinfo
+ *
+ *
+ * -------------------------------------------------------------
+ */
+#if !defined( PTHREAD_H )
+#define PTHREAD_H
+
+#ifdef _WIN32
+/*
+ * Disable following warnings when including Windows headers
+ *
+ * warning C4115: named type definition in parentheses
+ * warning C4116: unnamed type definition in parentheses
+ * warning C4127: conditional expression is constant
+ * warning C4201: nonstandard extension used : nameless struct/union
+ * warning C4214: nonstandard extension used : bit field types other than int
+ * warning C4514: unreferenced inline function has been removed
+ */
+#pragma warning( disable : 4115 4116 4127 4201 4214 4514)
#endif
+/*
+ * -----------------
+ * autoconf switches
+ * -----------------
+ */
+
#if HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
@@ -58,299 +224,730 @@ struct timespec {
#define SIG_SETMASK 2
#endif /* SIG_SETMASK */
-#define PTHREAD_STACK_MIN 65535
-/* Thread scheduling policies */
+#include <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 */
diff --git a/sched.c b/sched.c
index e3c3ae7..4680b8f 100644
--- a/sched.c
+++ b/sched.c
@@ -15,7 +15,9 @@
static int
is_attr(const pthread_attr_t *attr)
{
- return (attr == NULL || attr->valid != _PTHREAD_ATTR_VALID) ? 1 : 0;
+ return (attr == NULL ||
+ *attr == NULL ||
+ (*attr)->valid != _PTHREAD_ATTR_VALID) ? 1 : 0;
}
int
@@ -27,11 +29,12 @@ pthread_attr_setschedparam(pthread_attr_t *attr,
return EINVAL;
}
- attr->priority = param->sched_priority;
+ (*attr)->priority = param->sched_priority;
return 0;
}
-int pthread_attr_getschedparam(const pthread_attr_t *attr,
+int
+pthread_attr_getschedparam(const pthread_attr_t *attr,
struct sched_param *param)
{
if (is_attr(attr) != 0 || param == NULL)
@@ -39,7 +42,7 @@ int pthread_attr_getschedparam(const pthread_attr_t *attr,
return EINVAL;
}
- param->sched_priority = attr->priority;
+ param->sched_priority = (*attr)->priority;
return 0;
}
@@ -47,7 +50,7 @@ int pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param)
{
/* Validate the thread id. */
- if (_PTHREAD_VALID(thread) < 0)
+ if (thread == NULL || thread->threadH == 0)
{
return EINVAL;
}
@@ -72,7 +75,8 @@ int pthread_setschedparam(pthread_t thread, int policy,
}
/* This is practically guaranteed to return TRUE. */
- (void) SetThreadPriority(thread->win32handle, param->sched_priority);
+ (void) SetThreadPriority(thread->threadH, param->sched_priority);
+
return 0;
}
@@ -82,7 +86,7 @@ int pthread_getschedparam(pthread_t thread, int *policy,
int prio;
/* Validate the thread id. */
- if (_PTHREAD_VALID(thread) != 0)
+ if (thread == NULL || thread->threadH == 0)
{
return EINVAL;
}
@@ -97,7 +101,7 @@ int pthread_getschedparam(pthread_t thread, int *policy,
*policy = SCHED_OTHER;
/* Fill out the sched_param structure. */
- prio = GetThreadPriority(thread->win32handle);
+ prio = GetThreadPriority(thread->threadH);
if (prio == THREAD_PRIORITY_ERROR_RETURN)
{
return EINVAL;
diff --git a/semaphore.c b/semaphore.c
new file mode 100644
index 0000000..c163336
--- /dev/null
+++ b/semaphore.c
@@ -0,0 +1,254 @@
+/*
+ * -------------------------------------------------------------
+ *
+ * Module: semaphore.c
+ *
+ * Purpose:
+ * Semaphores aren't actually part of the PThreads standard.
+ * They are defined by the POSIX Standard:
+ *
+ * POSIX 1003.1b-1993 (POSIX.1b)
+ *
+ * They are supposed to follow the older UNIX convention for
+ * reporting errors. That is, on failure they are supposed
+ * to return a value of -1 and store the appropriate error
+ * number into 'errno'.
+ * HOWEVER,errno cannot be modified in a multithreaded
+ * program on WIN32; therefore, the value is returned as
+ * the function value.
+ * It is recommended that you compare for zero (0) for success
+ * instead of -1 for failure when checking the status of
+ * these functions.
+ *
+ * Contents:
+ * Public Methods Author
+ * -------------- ------
+ * sem_init John E. Bossom Mar 1998
+ * sem_destroy John E. Bossom Mar 1998
+ * sem_trywait John E. Bossom Mar 1998
+ * sem_wait John E. Bossom Mar 1998
+ * sem_post John E. Bossom Mar 1998
+ *
+ * Private Methods
+ * ---------------
+ *
+ * -------------------------------------------------------------
+ */
+#include <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 */
diff --git a/sync.c b/sync.c
index 069d10a..57f56b8 100644
--- a/sync.c
+++ b/sync.c
@@ -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;
}
+
diff --git a/tsd.c b/tsd.c
index cc0b98e..72a03ea 100644
--- a/tsd.c
+++ b/tsd.c
@@ -5,202 +5,306 @@
* POSIX thread functions which implement thread-specific data (TSD).
*/
-/*
- * Why we can't use Win32 TLS
- * --------------------------
- *
- * In a word: Destructors
- *
- * POSIX 1003.1 1996, Section 17 allows for optional destructor functions
- * to be associated with each key value.
- *
- * This is my (revised) understanding of how destructors work:
- *
- * A key is created by a single thread, which then provides in every
- * existing thread a TSD matching the same key, but initialised
- * to NULL. Each new thread will also get a matching key with value NULL.
- * The creating thread can optionally associate a function, called a
- * destructor, with the key.
- *
- * When each thread exits, it calls the destructor function, which
- * will then perform an action on that threads key value
- * only. (Previously I thought that only the key creating thread ran
- * the destructor on the key in all threads. That proposition is
- * sounding scarier by the minute.)
- *
- * SOME APPROACHES TO MANAGING TSD MEMORY
- *
- * We could simply allocate enough memory on process startup to hold
- * all possible data for all possible threads.
- *
- * We could allocate memory for just a table to hold a single pointer
- * for each of POSIX_THREAD_KEYS_MAX keys. pthread_key_create() could then
- * allocate space for POSIX_THREADS_MAX key values in one hit and store
- * the location of the array in the first table.
- *
- * The standard also suggests that each thread might store key/value pairs
- * on its private stack. This seems like a good idea. I had concerns about
- * memory leaks and key re-use if a key was deleted, but the standard talks
- * at length on this and basically says it's up to the application to
- * make sure everything goes smoothly here, making sure that proper cleanup
- * is done before a key is deleted. (section B.17.1.3 in particular)
- *
- * One more thing to note: destructors must never be called on deleted keys.
- */
-
-#include <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> */
+