summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog68
-rw-r--r--MAINTAINERS2
-rw-r--r--cancel.c241
-rw-r--r--condvar.c705
-rw-r--r--create.c146
-rw-r--r--dll.c113
-rw-r--r--exit.c43
-rw-r--r--global.c10
-rw-r--r--implement.h273
-rw-r--r--misc.c210
-rw-r--r--private.c411
-rw-r--r--pthread.h949
-rw-r--r--sync.c121
-rw-r--r--tsd.c307
14 files changed, 3344 insertions, 255 deletions
diff --git a/ChangeLog b/ChangeLog
index 04b46ac..c28edd6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,19 +1,79 @@
-1998-12-11 Ben Elliston <bje@toilet.to.cygnus.com>
-
- * README: Update info about subscribing to the mailing list.
+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/cancel.c b/cancel.c
index 9dd99c6..9364c47 100644
--- a/cancel.c
+++ b/cancel.c
@@ -10,6 +10,245 @@
#include "pthread.h"
#include "implement.h"
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+int
+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 self;
+ int result;
+
+ if (((self = pthread_self ()) != NULL) &&
+ (state == PTHREAD_CANCEL_ENABLE ||
+ state == PTHREAD_CANCEL_DISABLE))
+ {
+
+ *oldstate = self->cancelState;
+ self->cancelState = state;
+ result = 0;
+
+ }
+ else
+ {
+ result = EINVAL;
+ }
+
+ return (result);
+
+} /* pthread_setcancelstate */
+
+
+int
+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 self;
+ int result;
+
+ if (((self = pthread_self ()) != NULL) &&
+ (type == PTHREAD_CANCEL_DEFERRED ||
+ type == PTHREAD_CANCEL_ASYNCHRONOUS))
+ {
+
+ *oldtype = self->cancelType;
+ self->cancelType = type;
+ result = 0;
+
+ }
+ else
+ {
+ result = EINVAL;
+ }
+
+ return (result);
+
+} /* 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
+ *
+ * ------------------------------------------------------
+ */
+{
+ pthread_t self;
+
+ if ((self = pthread_getspecific (_pthread_selfThreadKey)) != NULL)
+ {
+
+ 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);
+ }
+ }
+ }
+
+} /* pthread_testcancel */
+
+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',
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result;
+
+ if (thread != NULL)
+ {
+
+ if (!SetEvent (thread->cancelEvent))
+ {
+ result = ESRCH;
+ }
+ else
+ {
+ result = 0;
+ }
+
+ }
+ else
+ {
+ result = ESRCH;
+ }
+
+ return (result);
+}
+
+/* </JEB> */
+
+#if 0 /* Pre Bossom */
+
+#include <errno.h>
+
+#include "pthread.h"
+#include "implement.h"
+
int
pthread_setcancelstate(int state,
int *oldstate)
@@ -82,3 +321,5 @@ pthread_testcancel(void)
}
/* Never reached. */
}
+
+#endif /* Pre Bossom */
diff --git a/condvar.c b/condvar.c
index 3dc76d4..f12c5e3 100644
--- a/condvar.c
+++ b/condvar.c
@@ -5,6 +5,709 @@
* This translation unit implements condition variables and their primitives.
*/
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "pthread.h"
+#include "implement.h"
+
+int
+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.
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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)
+ /*
+ * ------------------------------------------------------
+ * 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.
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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 (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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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_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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result;
+
+ if ((attr != NULL && *attr != NULL) &&
+ ((pshared == PTHREAD_PROCESS_SHARED) ||
+ (pshared == PTHREAD_PROCESS_PRIVATE)))
+ {
+
+
+ if (pshared == PTHREAD_PROCESS_SHARED)
+ {
+
+#if !defined( _POSIX_THREAD_PROCESS_SHARED )
+ result = ENOSYS;
+ pshared = PTHREAD_PROCESS_PRIVATE;
+#else
+ result = 0;
+
+#endif /* _POSIX_THREAD_PROCESS_SHARED */
+
+ }
+ else
+ {
+ result = 0;
+ }
+ (*attr)->pshared = pshared;
+
+ }
+ else
+ {
+ result = EINVAL;
+
+ }
+
+ return (result);
+
+} /* pthread_condattr_setpshared */
+
+
+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 = EAGAIN;
+ pthread_cond_t cv;
+
+ if ((attr != NULL && *attr != NULL) &&
+ ((*attr)->pshared == PTHREAD_PROCESS_SHARED))
+ {
+ /*
+ * Creating condition variable that can be shared between
+ * processes.
+ */
+ result = ENOSYS;
+
+ goto FAIL0;
+ }
+
+ cv = (pthread_cond_t) calloc (1, sizeof (*cv));
+
+ if (cv != NULL)
+ {
+ result = ENOMEM;
+ goto FAIL0;
+ }
+
+ cv->waiters = 0;
+ cv->wasBroadcast = FALSE;
+
+ if (sem_init (&(cv->sema), 0, 1) != 0)
+ {
+ goto FAIL0;
+ }
+ if (pthread_mutex_init (&(cv->waitersLock), NULL) != 0)
+ {
+ goto FAIL1;
+ }
+
+ cv->waitersDone = CreateEvent (
+ 0,
+ (int) FALSE, /* manualReset */
+ (int) FALSE, /* setSignaled */
+ NULL);
+
+ if (cv->waitersDone == NULL)
+ {
+ goto FAIL2;
+ }
+
+ result = 0;
+
+ goto DONE;
+
+ /*
+ * -------------
+ * 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 */
+
+
+int
+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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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);
+
+ *cond = NULL;
+ }
+
+ return (result);
+}
+
+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 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);
+ }
+
+ if ((result = pthread_mutex_lock (&(cv->waitersLock))) == 0)
+ {
+ /*
+ * 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;
+ }
+ }
+ }
+
+ /*
+ * 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);
+
+} /* pthread_cond_wait */
+
+
+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 * 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 result = 0;
+ pthread_cond_t cv = *cond;
+
+ /*
+ * If there aren't any waiters, then this is a no-op.
+ */
+ if (cv->waiters > 0)
+ {
+
+ result = sem_post (&(cv->sema));
+ }
+
+ return (result);
+
+} /* pthread_cond_signal */
+
+int
+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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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--)
+ {
+
+ result = sem_post (&(cv->sema));
+ }
+
+ 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> */
+
+
+#if 0 /* Pre Bossom */
+
#include <errno.h>
#include <windows.h>
@@ -202,3 +905,5 @@ pthread_cond_destroy(pthread_cond_t *cv)
return pthread_mutex_destroy(&cv->waiters_count_lock);
}
+
+#endif /* Pre Bossom */
diff --git a/create.c b/create.c
index 6119198..d5f097e 100644
--- a/create.c
+++ b/create.c
@@ -6,6 +6,149 @@
* thread.
*/
+#include "pthread.h"
+#include "implement.h"
+
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+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)
+ {
+ goto FAIL0;
+ }
+
+ if ((parms = (ThreadParms *) malloc (sizeof (*parms))) ==
+ NULL)
+ {
+ goto FAIL0;
+ }
+
+ parms->tid = thread;
+ parms->start = start;
+ parms->arg = arg;
+
+ if (attr != NULL && *attr != NULL)
+ {
+ stackSize = (*attr)->stacksize;
+ thread->detachState = (*attr)->detachstate;
+
+ }
+ else
+ {
+ /*
+ * Default stackSize
+ */
+ stackSize = 0;
+ }
+
+ thread->state = run
+ ? PThreadStateInitial
+ : PThreadStateSuspended;
+
+ thread->keys = NULL;
+ thread->threadH = (HANDLE)
+ _beginthreadex (
+ (void *) NULL, /* No security info */
+ (unsigned) stackSize, /* default stack size */
+ (unsigned (__stdcall *) (void *)) threadStart,
+ parms,
+ (unsigned) run ? 0 : CREATE_SUSPENDED,
+ (unsigned *) &(thread->thread));
+
+ result = (thread->threadH != 0) ? 0 : EAGAIN;
+
+ /*
+ * Fall Through Intentionally
+ */
+
+ /*
+ * ------------
+ * Failure Code
+ * ------------
+ */
+
+FAIL0:
+ if (result != 0)
+ {
+
+ threadDestroy (thread);
+ thread = NULL;
+
+ if (parms != NULL)
+ {
+ free (parms);
+ }
+ }
+ *tid = thread;
+
+ return (result);
+
+} /* pthread_create */
+
+/* </JEB> */
+
+
+#if 0 /* Pre Bossom */
+
#include <errno.h>
#include <windows.h>
@@ -146,5 +289,4 @@ pthread_create(pthread_t *thread,
return ret;
}
-
-
+#endif /* Pre Bossom */
diff --git a/dll.c b/dll.c
index bb3d7ef..940a0dd 100644
--- a/dll.c
+++ b/dll.c
@@ -5,6 +5,112 @@
* This translation unit implements DLL initialisation.
*/
+#include <windows.h>
+#include <malloc.h>
+#include "pthread.h"
+#include "implement.h"
+
+
+/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */
+BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL;
+
+/* Handle to kernel32.dll */
+static HINSTANCE _pthread_h_kernel32;
+
+#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 (fdwReason)
+ {
+
+ case DLL_PROCESS_ATTACH:
+ /*
+ * 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");
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /*
+ * A thread is being created
+ */
+ result = TRUE;
+ break;
+
+ 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)
+ {
+#if defined( KLUDGE )
+ _pthread_cleanupStack ();
+#endif /* KLUDGE */
+
+ 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);
+
+} /* DllMain */
+
+
+
+#if 0 /* Pre Bossom */
+
/* We use the DLL entry point function to set up per thread storage
specifically to hold the threads own thread ID.
@@ -14,11 +120,6 @@
*/
-#include <windows.h>
-#include <malloc.h>
-#include "pthread.h"
-#include "implement.h"
-
/* Global index for TLS data. */
DWORD _pthread_threadID_TlsIndex;
@@ -78,3 +179,5 @@ BOOL WINAPI PthreadsEntryPoint(HINSTANCE dllHandle,
return TRUE;
}
+
+#endif /* Pre Bossom */
diff --git a/exit.c b/exit.c
index 3472dca..bb46165 100644
--- a/exit.c
+++ b/exit.c
@@ -11,6 +11,47 @@
#include "pthread.h"
#include "implement.h"
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+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
+ *
+ * ------------------------------------------------------
+ */
+{
+ _pthread_callUserDestroyRoutines(pthread_getspecific(_pthread_selfThreadKey));
+
+ _endthreadex ((unsigned) value_ptr);
+
+ return (0);
+
+} /* pthread_exit */
+
+/* </JEB> */
+
+
+#if 0 /* Pre Bossom */
+
void
_pthread_vacuum(void)
{
@@ -72,3 +113,5 @@ pthread_exit(void * value)
{
_pthread_exit(pthread_self(), value, 0);
}
+
+#endif /* Pre Bossom */
diff --git a/global.c b/global.c
index a2ca988..01a1a7c 100644
--- a/global.c
+++ b/global.c
@@ -11,6 +11,14 @@
#include "pthread.h"
#include "implement.h"
+
+int _pthread_processInitialized = FALSE;
+pthread_key_t _pthread_selfThreadKey = NULL;
+pthread_key_t _pthread_cleanupKey = NULL;
+
+
+#if 0 /* Pre Bossom */
+
/* POSIX run-time invariant values. (Currently POSIX minimum values)
Making these constants will mean that applications remain binary
@@ -81,3 +89,5 @@ pthread_key_t _pthread_key_reuse[_PTHREAD_MAX_KEYS];
/* Index to the first available reusable pthread_key_t. */
int _pthread_key_reuse_top;
+
+#endif /* Pre Bossom */
diff --git a/implement.h b/implement.h
index db6b5a2..523f475 100644
--- a/implement.h
+++ b/implement.h
@@ -1,12 +1,281 @@
/*
* 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
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+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;
+};
+
+
+struct pthread_attr_t_ {
+ void *stackaddr;
+ size_t stacksize;
+ int detachstate;
+};
+
+
+struct pthread_key_t_ {
+ DWORD key;
+ void (*destructor) (void *);
+ pthread_mutex_t threadsLock;
+ void *threads;
+};
+
+
+struct pthread_mutexattr_t_ {
+ int pshared;
+};
+
+
+struct pthread_mutex_t_ {
+ int valid;
+ CRITICAL_SECTION cs;
+ };
+
+
+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 */
+};
+
+
+struct pthread_condattr_t_ {
+ int pshared;
+};
+
+
+struct pthread_once_t_ {
+ unsigned short flag;
+ pthread_mutex_t lock;
+};
+
+
+typedef struct ThreadParms ThreadParms;
+typedef struct ThreadKeyAssoc ThreadKeyAssoc;
+
+
+struct ThreadParms {
+ pthread_t tid;
+ void *(*start) (void *);
+ void *arg;
+};
+
+
+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;
+};
+
+
+/*
+ * --------------------------------------------------------------
+ * 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 */ \
+ ) )
+
+/*
+ * 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 )
+
+
+#define PTHREAD_SERVICES_FACILITY 0xBAD
+#define PTHREAD_SERVICES_ERROR 0xDEED
+
+
+/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */
+extern BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION);
+
+/* Declared in global.c */
+extern int _pthread_processInitialized;
+extern pthread_key_t _pthread_selfThreadKey;
+extern pthread_key_t _pthread_cleanupKey;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * =====================
+ * =====================
+ * Forward Declarations
+ * =====================
+ * =====================
+ */
+int _pthread_processInitialize (void);
+
+void _pthread_processTerminate (void);
+
+void _pthread_threadDestroy (pthread_t tid);
+
+void _pthread_cleanupStack (void);
+
+void *_pthread_threadStart (ThreadParms * threadParms);
+
+void _pthread_callUserDestroyRoutines (pthread_t thread);
+
+int _pthread_tkAssocCreate (ThreadKeyAssoc ** assocP,
+ pthread_t thread,
+ pthread_key_t key);
+
+void _pthread_tkAssocDestroy (ThreadKeyAssoc * assoc);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* </JEB> */
+
+
+#if 0 /* Pre Bossom */
+
/* Use internally to initialise const ints and thread admin array sizes. */
#define _PTHREAD_MAX_THREADS 128
#define _PTHREAD_MAX_KEYS 128
@@ -194,4 +463,6 @@ extern pthread_key_t _pthread_key_reuse[];
/* Index to the first available reusable pthread_key_t. */
extern int _pthread_key_reuse_top;
+#endif /* Pre Bossom */
+
#endif /* _IMPLEMENT_H */
diff --git a/misc.c b/misc.c
index 659a4eb..e244701 100644
--- a/misc.c
+++ b/misc.c
@@ -39,6 +39,214 @@ pthread_once(pthread_once_t *once_control,
return 0;
}
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+pthread_t
+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 self = NULL;
+ /*
+ * need to ensure there always is a self
+ */
+
+ if ((self = pthread_getspecific (_pthread_selfThreadKey)) == NULL)
+ {
+ /*
+ * 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 (self);
+
+} /* pthread_self */
+
+int
+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
+ *
+ * ------------------------------------------------------
+ */
+{
+ 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> */
+
+#if 0 /* Pre Bossom */
+
pthread_t
pthread_self(void)
{
@@ -61,3 +269,5 @@ pthread_equal(pthread_t t1, pthread_t t2)
{
return (t1 == t2);
}
+
+#endif /* Pre Bossom */
diff --git a/private.c b/private.c
index 32116ef..9891660 100644
--- a/private.c
+++ b/private.c
@@ -11,6 +11,415 @@
#include "pthread.h"
#include "implement.h"
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+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;
+
+ /*
+ * Initialize Keys
+ */
+ if ((pthread_key_create (&_pthread_selfThreadKey, NULL) != 0) ||
+ (pthread_key_create (&_pthread_cleanupKey, NULL) != 0))
+ {
+
+ _pthread_processTerminate ();
+ }
+
+ 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);
+
+ _pthread_selfThreadKey = NULL;
+ }
+
+ if (_pthread_cleanupKey != NULL)
+ {
+ /*
+ * Release _pthread_cleanupKey
+ */
+ pthread_key_delete (_pthread_cleanupKey);
+
+ _pthread_cleanupKey = NULL;
+ }
+
+ _pthread_processInitialized = FALSE;
+ }
+
+} /* processTerminate */
+
+
+
+void *
+_pthread_threadStart (ThreadParms * threadParms)
+{
+ pthread_t tid;
+ void *(*start) (void *);
+ void *arg;
+
+ int status;
+
+ tid = threadParms->tid;
+ start = threadParms->start;
+ arg = threadParms->arg;
+
+ free (threadParms);
+
+ pthread_setspecific (_pthread_selfThreadKey, tid);
+
+ __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;
+ }
+
+ pthread_exit ((void *) status);
+
+ return ((void *) status);
+
+} /* threadStart */
+
+
+void
+_pthread_threadDestroy (pthread_t thread)
+{
+ if (thread != NULL)
+ {
+
+ callUserDestroyRoutines (thread);
+
+ if (thread->cancelEvent != NULL)
+ {
+ CloseHandle (thread->cancelEvent);
+ }
+
+ free (thread);
+ }
+
+} /* threadDestroy */
+
+#if defined( KLUDGE )
+
+void
+_pthread_cleanupStack (void)
+{
+ while (pthread_pop_cleanup (1))
+ {
+ }
+
+} /* cleanupStack */
+
+#endif /* KLUDGE */
+
+int
+_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
+ * -------------------------------------------------------------------
+ */
+{
+ 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 (assoc == NULL)
+ {
+ result = -1;
+ goto FAIL0;
+ }
+
+ if ((result = pthread_mutex_init (&(assoc->lock), NULL)) !=
+ 0)
+ {
+ goto FAIL1;
+ }
+
+ assoc->thread = thread;
+ assoc->key = key;
+
+ /*
+ * Register assoc with key
+ */
+ if ((result = pthread_mutex_lock (&(key->threadsLock))) !=
+ 0)
+ {
+ goto FAIL2;
+ }
+
+ assoc->nextThread = (ThreadKeyAssoc *) key->threads;
+ key->threads = (void *) assoc;
+
+ 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);
+
+FAIL0:
+
+ return (result);
+
+} /* tkAssocCreate */
+
+
+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_mutex_destroy (&(assoc->lock));
+
+ free (assoc);
+ }
+
+} /* 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> */
+
+
+#if 0 /* Pre Bossom */
+
/* Thread ID management.
---------------------
@@ -141,3 +550,5 @@ _pthread_delete_thread(_pthread_t * thread)
return EINVAL;
}
+
+#endif /* Pre Bossom */
diff --git a/pthread.h b/pthread.h
index ef22d80..458ef6e 100644
--- a/pthread.h
+++ b/pthread.h
@@ -1,33 +1,191 @@
/* 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
+ *
+ * Limitations
+ * ===========
+ * The following functions are not implemented:
+ *
+ * ---------------------------
+ * RealTime Scheduling:
+ * ---------------------------
+ * 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_mutex_getprioceiling
+ * pthread_mutex_setprioceiling
+ * pthread_mutex_attr_getprioceiling
+ * pthread_mutex_attr_getprotocol
+ * pthread_mutex_attr_setprioceiling
+ * pthread_mutex_attr_setprotocol
+ * pthread_setschedparam
+ * sched_get_priority_max
+ * sched_get_priority_min
+ *
+ * ---------------------------
+ * 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
+ * pthread_sigmask
+ * 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 +216,604 @@ struct timespec {
#define SIG_SETMASK 2
#endif /* SIG_SETMASK */
-#define PTHREAD_STACK_MIN 65535
-
-/* Thread scheduling policies */
-
-#define SCHED_OTHER 0
-#define SCHED_FIFO 1
-#define SCHED_RR 2
-
-#define SCHED_MIN SCHED_OTHER
-#define SCHED_MAX SCHED_RR
-
-/* 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)
-
-#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;
-
-
-typedef DWORD pthread_key_t;
-
-/* Related constants */
-typedef struct {
- long valid;
+#include <process.h>
+#include <errno.h>
-#ifdef _POSIX_THREAD_ATTR_STACKSIZE
- size_t stacksize; /* PTHREAD_STACK_MIN */
+#ifdef _WIN32
+/*
+ * Re-enable all but 4127, 4514
+ */
+#pragma warning( default : 4115 4116 4201 4214)
#endif
- int detachedstate; /* PTHREAD_CREATE_DETACHED
- PTHREAD_CREATE_JOINABLE */
+#if !defined( TRUE )
+#define TRUE !FALSE
+#define FALSE 0
+#endif /* !TRUE */
-#if HAVE_SIGSET_T
- sigset_t sigmask;
-#endif /* HAVE_SIGSET_T */
- int priority;
+#include <semaphore.h>
+#include <sched.h>
-} pthread_attr_t;
-
-/* 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;
-};
-
-enum { SIGNAL, BROADCAST, NUM_EVENTS };
-
-typedef struct {
- /* Signal and broadcast event HANDLEs. */
- HANDLE events[NUM_EVENTS];
-
- /* Count of the number of waiters. */
- unsigned waiters_count;
-
- /* Serialize access to waiters_count. */
- pthread_mutex_t waiters_count_lock;
-} pthread_cond_t;
-
-typedef struct { void * dummy; } pthread_condattr_t;
-typedef struct { void * dummy; } pthread_mutexattr_t;
-
-typedef struct {
- 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));
-
-int pthread_create(pthread_t *thread,
- const pthread_attr_t *attr,
- void * (*start_routine) (void *),
- void * arg);
+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
-void pthread_exit(void *value);
-
-pthread_t pthread_self(void);
+#define _POSIX_THREAD_ATTR_STACKSIZE
-int pthread_equal(pthread_t t1, pthread_t t2);
+#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 }
+
+
+/*
+ * ====================
+ * ====================
+ * Scheduling
+ * ====================
+ * ====================
+ */
-int pthread_join(pthread_t thread, void ** valueptr);
+/* Thread scheduling policies */
-int pthread_detach(pthread_t thread);
+#define SCHED_OTHER 0
+#define SCHED_FIFO 1
+#define SCHED_RR 2
-int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
+#define SCHED_MIN SCHED_OTHER
+#define SCHED_MAX SCHED_RR
-/* Functions for manipulating thread attribute objects. */
+ struct sched_param {
+ int sched_priority;
+ };
+
+
+
+/* There are three implementations of cancel cleanup.
+ *
+ * C
+ * C++ (as per Cygwin32 or Mingw32)
+ * WIN32 SEH or C++
+ */
+
+#ifndef __cplusplus
+
+/*
+ * C implementation of PThreads cancel cleanup
+ */
+ typedef struct pthread_cleanup_t pthread_cleanup_t;
+
+ struct pthread_cleanup_t
+ {
+ void (*routine) (void *);
+ void *arg;
+ pthread_cleanup_t *prev;
+ };
+
+#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_init(pthread_attr_t *attr);
+#endif /* _WIN32 */
-int pthread_attr_destroy(pthread_attr_t *attr);
+#endif /* !__cplusplus */
-int pthread_attr_setstacksize(pthread_attr_t *attr,
- size_t stacksize);
+/*
+ * ===============
+ * ===============
+ * Methods
+ * ===============
+ * ===============
+ */
+
+/*
+ * PThread Attribute Functions
+ */
+int pthread_attr_init (pthread_attr_t * attr);
-int pthread_attr_getstacksize(const pthread_attr_t *attr,
- size_t *stacksize);
+int pthread_attr_destroy (pthread_attr_t * attr);
-int pthread_attr_setstackaddr(pthread_attr_t *attr,
- void *stackaddr);
+int pthread_attr_getdetachstate (const pthread_attr_t * attr,
+ int *detachstate);
-int pthread_attr_getstackaddr(const pthread_attr_t *attr,
- void **stackaddr);
+int pthread_attr_getstackaddr (const pthread_attr_t * attr,
+ void **stackaddr);
-int pthread_attr_getschedparam(const pthread_attr_t *attr,
- struct sched_param *param);
+int pthread_attr_getstacksize (const pthread_attr_t * attr,
+ size_t * stacksize);
-int pthread_attr_setschedparam(pthread_attr_t *attr,
- const struct sched_param *param);
+int pthread_attr_setdetachstate (pthread_attr_t * attr,
+ int detachstate);
-int pthread_attr_getdetachstate(const pthread_attr_t *attr,
- int *detachstate);
+int pthread_attr_setstackaddr (pthread_attr_t * attr,
+ void *stackaddr);
-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_setstacksize (pthread_attr_t * attr,
+ size_t stacksize);
-int pthread_getschedparam(pthread_t thread,
- int *policy,
- struct sched_param *param);
+/*
+ * PThread Functions
+ */
+int pthread_create (pthread_t * tid,
+ const pthread_attr_t * attr,
+ void *(*start) (void *),
+ void *arg);
-int sched_get_priority_min(int policy);
+int pthread_detach (pthread_t tid);
-int sched_get_priority_max(int policy);
+int pthread_equal (pthread_t t1,
+ pthread_t t2);
-int pthread_setcancelstate(int state,
- int *oldstate);
+int pthread_exit (void *value_ptr);
-int pthread_setcanceltype(int type,
- int *oldtype);
+int pthread_join (pthread_t thread,
+ void **value_ptr);
-/* Functions for manipulating cond. var. attribute objects. */
+pthread_t pthread_self (void);
-int pthread_condattr_init(pthread_condattr_t *attr);
+int pthread_cancel (pthread_t thread);
-int pthread_condattr_setpshared(pthread_condattr_t *attr,
- int pshared);
+pthread_cleanup_t *pthread_pop_cleanup (int execute);
-int pthread_condattr_getpshared(pthread_condattr_t *attr,
- int *pshared);
+void pthread_push_cleanup (pthread_cleanup_t * cleanup,
+ void (*routine) (void *),
+ void *arg);
-int pthread_condattr_destroy(pthread_condattr_t *attr);
+int pthread_setcancelstate (int state,
+ int *oldstate);
-/* Functions for manipulating mutex attribute objects. */
+int pthread_setcanceltype (int type,
+ int *oldtype);
-int pthread_mutexattr_init(pthread_mutexattr_t *attr);
+void pthread_testcancel (void);
-int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
+int pthread_once (pthread_once_t * once_control,
+ void (*init_routine) (void));
-/* Primitives for condition variables. */
+/*
+ * Thread Specific Data Functions
+ */
+int pthread_key_create (pthread_key_t * key,
+ void (*destructor) (void *));
-int pthread_cond_init(pthread_cond_t *cv,
- const pthread_condattr_t *attr);
+int pthread_key_delete (pthread_key_t key);
-int pthread_cond_broadcast(pthread_cond_t *cv);
+int pthread_setspecific (pthread_key_t key,
+ const void *value);
-int pthread_cond_signal(pthread_cond_t *cv);
+void *pthread_getspecific (pthread_key_t key);
-int pthread_cond_timedwait(pthread_cond_t *cv,
- pthread_mutex_t *mutex,
- const struct timespec *abstime);
-int pthread_cond_wait(pthread_cond_t *cv,
- pthread_mutex_t *mutex);
+/*
+ * Mutex Attribute Functions
+ */
+int pthread_mutexattr_init (pthread_mutexattr_t * attr);
-int pthread_cond_destroy(pthread_cond_t *cv);
+int pthread_mutexattr_destroy (pthread_mutexattr_t * attr);
-/* Primitives for mutexes. */
+int pthread_mutexattr_getpshared (const pthread_mutexattr_t
+ * attr,
+ int *pshared);
-int pthread_mutex_init(pthread_mutex_t *mutex,
- const pthread_mutexattr_t *attr);
+int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
+ int pshared);
-int pthread_mutex_destroy(pthread_mutex_t *mutex);
+/*
+ * Mutex Functions
+ */
+int pthread_mutex_init (pthread_mutex_t * mutex,
+ const pthread_mutexattr_t * attr);
-int pthread_mutex_lock(pthread_mutex_t *mutex);
+int pthread_mutex_destroy (pthread_mutex_t * mutex);
-int pthread_mutex_trylock(pthread_mutex_t *mutex);
+int pthread_mutex_lock (pthread_mutex_t * mutex);
-int pthread_mutex_unlock(pthread_mutex_t *mutex);
+int pthread_mutex_trylock (pthread_mutex_t * mutex);
-/* Primitives for thread-specific data (TSD). */
+int pthread_mutex_unlock (pthread_mutex_t * mutex);
-int pthread_key_create(pthread_key_t *key,
- void (*destructor)(void *));
+/*
+ * Condition Variable Attribute Functions
+ */
+int pthread_condattr_init (pthread_condattr_t * attr);
-int pthread_setspecific(pthread_key_t key, void *value);
+int pthread_condattr_destroy (pthread_condattr_t * attr);
-void *pthread_getspecific(pthread_key_t key);
+int pthread_condattr_getpshared (const pthread_condattr_t * attr,
+ int *pshared);
-int pthread_key_delete(pthread_key_t key);
+int pthread_condattr_setpshared (pthread_condattr_t * attr,
+ int pshared);
-/* Signal handling. */
+/*
+ * Condition Variable Functions
+ */
+int pthread_cond_init (pthread_cond_t * cond,
+ const pthread_condattr_t * attr);
-#if HAVE_SIGSET_T
-int pthread_sigmask(int how,
- const sigset_t *set,
- sigset_t *oset);
-#endif /* HAVE_SIGSET_T */
+int pthread_cond_destroy (pthread_cond_t * cond);
-/* Thread cancelation functions. */
+int pthread_cond_wait (pthread_cond_t * cond,
+ pthread_mutex_t * mutex);
-void pthread_testcancel(void);
+int pthread_cond_timedwait (pthread_cond_t * cond,
+ pthread_mutex_t * mutex,
+ const struct timespec *abstime);
-int pthread_cancel(pthread_t thread);
+int pthread_cond_signal (pthread_cond_t * cond);
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+int pthread_cond_broadcast (pthread_cond_t * cond);
-/* 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;
+/*
+ * Scheduling
+ */
-extern const int _pthread_create_joinable;
-extern const int _pthread_create_detached;
+int pthread_setschedparam (pthread_t thread,
+ int policy,
+ const struct sched_param *param);
-extern const int _pthread_cancel_enable;
-extern const int _pthread_cancel_disable;
+int pthread_getschedparam (pthread_t thread,
+ int *policy,
+ struct sched_param *param);
-extern const int _pthread_cancel_asynchronous;
-extern const int _pthread_cancel_deferred;
+int sched_get_priority_min (int policy);
-#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 sched_get_priority_max (int policy);
-#define PTHREAD_CREATE_JOINABLE _pthread_create_joinable
-#define PTHREAD_CREATE_DETACHED _pthread_create_detached
+int pthread_attr_getschedparam (const pthread_attr_t *attr,
+ struct sched_param *param);
-/* Cancelability attributes */
-#define PTHREAD_CANCEL_ENABLE _pthread_cancel_enable
-#define PTHREAD_CANCEL_DISABLE _pthread_cancel_disable
+int pthread_attr_setschedparam (pthread_attr_t *attr,
+ const struct sched_param *param);
-#define PTHREAD_CANCEL_ASYNCHRONOUS _pthread_cancel_asynchronous
-#define PTHREAD_CANCEL_DEFERRED _pthread_cancel_deferred
+/*
+ * 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);
-/* 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.
+/*
+ * 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) ) )
- 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. */
+#define asctime_r( _tm, _buf ) \
+ ( strcpy( (_buf), asctime( (_tm) ) ), \
+ (_buf) )
-enum {
- _PTHREAD_HANDLER_POP_LIFO,
- _PTHREAD_HANDLER_POP_FIFO
-};
+#define ctime_r( _clock, _buf ) \
+ ( strcpy( (_buf), ctime( (_tm) ) ), \
+ (_buf) )
-enum {
- _PTHREAD_CLEANUP_STACK,
- _PTHREAD_DESTRUCTOR_STACK,
- _PTHREAD_FORKPREPARE_STACK,
- _PTHREAD_FORKPARENT_STACK,
- _PTHREAD_FORKCHILD_STACK
-};
+#define gmtime_r( _clock, _result ) \
+ ( *(_result) = *gmtime( (_clock) ), \
+ (_result) )
-#ifdef pthread_cleanup_push
-#undef pthread_cleanup_push
-#endif
+#define localtime_r( _clock, _result ) \
+ ( *(_result) = *localtime( (_clock) ), \
+ (_result) )
-#define pthread_cleanup_push(routine, arg) \
-{ \
- (void ) _pthread_handler_push(_PTHREAD_CLEANUP_STACK, \
- _PTHREAD_HANDLER_POP_LIFO, routine, arg);
+#define rand_r( _seed ) \
+ rand()
-#ifdef pthread_cleanup_pop
-#undef pthread_cleanup_pop
-#endif
-#define pthread_cleanup_pop(execute) \
- _pthread_handler_pop(_PTHREAD_CLEANUP_STACK, execute);\
-}
+#ifdef __cplusplus
+} /* End of extern "C" */
+#endif /* __cplusplus */
-#endif /* _PTHREADS_H */
diff --git a/sync.c b/sync.c
index 069d10a..3d64df9 100644
--- a/sync.c
+++ b/sync.c
@@ -6,6 +6,125 @@
* synchronisation.
*/
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+int
+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 result;
+
+ if (tid == NULL ||
+ tid->detachState == PTHREAD_CREATE_DETACHED)
+ {
+
+ result = EINVAL;
+
+ }
+ else
+ {
+ result = 0;
+ tid->detachState = PTHREAD_CREATE_DETACHED;
+ }
+
+ return (result);
+
+} /* pthread_detach */
+
+int
+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 result = 0;
+ pthread_t self;
+
+ self = pthread_self ();
+
+ if (pthread_equal (self, thread) == 0)
+ {
+ result = EDEADLK;
+
+ }
+ else
+ {
+ DWORD stat;
+
+ stat = WaitForSingleObject (thread->threadH, INFINITE);
+
+ if (stat != WAIT_OBJECT_0 &&
+ !GetExitCodeThread (thread->threadH, (LPDWORD) value_ptr))
+ {
+ result = ESRCH;
+ }
+ else
+ {
+ threadDestroy (thread);
+ }
+ }
+
+ return (result);
+
+} /* pthread_join */
+
+/* </JEB> */
+
+
+#if 0 /* Pre Bossom */
+
#include <errno.h>
/* POSIX STANDARD: A thread may pass a value pointer to some data via
@@ -186,3 +305,5 @@ pthread_detach(pthread_t thread)
return ret;
}
+
+#endif /* Pre Bossom */
diff --git a/tsd.c b/tsd.c
index cc0b98e..757ea9f 100644
--- a/tsd.c
+++ b/tsd.c
@@ -48,6 +48,312 @@
* One more thing to note: destructors must never be called on deleted keys.
*/
+#include "pthread.h"
+#include "implement.h"
+
+/*
+ * Code contributed by John E. Bossom <JEB>.
+ */
+
+int
+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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result = 0;
+
+ if ((*key = (pthread_key_t) calloc (1, sizeof (**key))) == NULL)
+ {
+ result = ENOMEM;
+ }
+ else if (((*key)->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES)
+ {
+ /*
+ * Create system key
+ */
+ result = EAGAIN;
+
+ free (*key);
+ *key = NULL;
+ }
+ else if (destructor != NULL)
+ {
+ /*
+ * 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;
+ }
+
+ return (result);
+}
+
+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,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result = 0;
+
+ 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));
+
+ 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);
+ }
+
+ return (result);
+}
+
+
+int
+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
+ *
+ * ------------------------------------------------------
+ */
+{
+ pthread_t self;
+ int result = 0;
+
+ if (key != _pthread_selfThreadKey)
+ {
+ /*
+ * 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
+ {
+ /*
+ * 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)
+ ? tkAssocCreate (&assoc, self, key)
+ : 0;
+ }
+ else
+ {
+ result = 0;
+ }
+
+ if (result == 0)
+ {
+ TlsSetValue (key->key, (LPVOID) value);
+ }
+ }
+ return (result);
+} /* pthread_setspecific */
+
+
+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> */
+
+
+#if 0 /* Pre Bossom */
+
#include <errno.h>
#include "pthread.h"
@@ -204,3 +510,4 @@ pthread_key_delete(pthread_key_t key)
return ret;
}
+#endif /* Pre Bossom */