From 95aa0a376d93ee021a6c085c71418e9f16513e0a Mon Sep 17 00:00:00 2001 From: rpj Date: Tue, 22 Dec 1998 15:59:24 +0000 Subject: Sun Dec 20 14:51:58 1998 Ross Johnson * misc.c (pthreadCancelableWait): New function by John Bossom. Non-stand ard 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. Cancella tion should cause the cleanup to run irrespective of the execute arg. * condvar.c (pthread_condattr_init): Replaced by John Bossom's version. (pthread_condattr_destroy): Replaced by John Bossom's version. (pthread_condattr_getpshared): Replaced by John Bossom's version. (pthread_condattr_setpshared): Replaced by John Bossom's version. (pthread_cond_init): Replaced by John Bossom's version. Fix comment (refered to mutex rather than condition variable). (pthread_cond_destroy): Replaced by John Bossom's version. (pthread_cond_wait): Replaced by John Bossom's version. (pthread_cond_timedwait): Replaced by John Bossom's version. (pthread_cond_signal): Replaced by John Bossom's version. (pthread_cond_broadcast): Replaced by John Bossom's version. Thu Dec 17 19:10:46 1998 Ross Johnson * tsd.c (pthread_key_create): Replaced by John Bossom's version. (pthread_key_delete): Replaced by John Bossom's version. (pthread_setspecific): Replaced by John Bossom's version. (pthread_getspecific): Replaced by John Bossom's version. Mon Dec 7 09:44:40 1998 Ross Johnson * cancel.c (pthread_setcancelstate): Replaced by John Bossom's version. (pthread_setcanceltype): Replaced by John Bossom's version. (pthread_testcancel): Replaced by John Bossom's version. (pthread_cancel): Replaced by John Bossom's version. * exit.c (pthread_exit): Replaced by John Bossom's version. * misc.c (pthread_self): Replaced by John Bossom's version. (pthread_equal): Replaced by John Bossom's version. * sync.c (pthread_detach): Replaced by John Bossom's version. (pthread_join): Replaced by John Bossom's version. * create.c (pthread_create): Replaced by John Bossom's version. * private.c (_pthread_processInitialize): New by John Bossom. (_pthread_processTerminate): Non-public function by John Bossom. (_pthread_threadStart): Non-public function by John Bossom. (_pthread_threadDestroy): Non-public function by John Bossom. (_pthread_cleanupStack): Non-public function by John Bossom. (_pthread_tkAssocCreate): Non-public function by John Bossom. (_pthread_tkAssocDestroy): Non-public function by John Bossom. (_pthread_callUserDestroyRoutines): Non-public function by John Bossom. * implement.h: Added John Bossom's non-API structures and declarations. * dll.c (PthreadsEntryPoint): Cast return value of GetProcAddress to resolve compile warning from MSVC. * dll.c (DLLmain): Replaced by John Bossom's version. * dll.c (PthreadsEntryPoint): Re-applied Anders Norlander's patch:- Initialize _pthread_try_enter_critical_section at startup and release kernel32 handle when DLL is being unloaded. --- ChangeLog | 68 ++++- MAINTAINERS | 2 + cancel.c | 241 +++++++++++++++ condvar.c | 705 ++++++++++++++++++++++++++++++++++++++++++++ create.c | 146 +++++++++- dll.c | 113 +++++++- exit.c | 43 +++ global.c | 10 + implement.h | 273 ++++++++++++++++- misc.c | 210 ++++++++++++++ private.c | 411 ++++++++++++++++++++++++++ pthread.h | 949 ++++++++++++++++++++++++++++++++++++++++++++---------------- sync.c | 121 ++++++++ tsd.c | 307 ++++++++++++++++++++ 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 - - * README: Update info about subscribing to the mailing list. +Sun Dec 20 14:51:58 1998 Ross Johnson + + * misc.c (pthreadCancelableWait): New function by John Bossom. Non-standard + but provides a hook that can be used to implement cancellation points in + applications that use this library. + + * pthread.h (pthread_cleanup_pop): C++ (non-WIN32) version uses + try/catch to emulate John Bossom's WIN32 __try/__finally behaviour. + In the WIN32 version __finally block, add a test for AbnormalTermination otherwise + cleanup is only run if the cleanup_pop execute arg is non-zero. Cancellation + should cause the cleanup to run irrespective of the execute arg. + + * condvar.c (pthread_condattr_init): Replaced by John Bossom's version. + (pthread_condattr_destroy): Replaced by John Bossom's version. + (pthread_condattr_getpshared): Replaced by John Bossom's version. + (pthread_condattr_setpshared): Replaced by John Bossom's version. + (pthread_cond_init): Replaced by John Bossom's version. + Fix comment (refered to mutex rather than condition variable). + (pthread_cond_destroy): Replaced by John Bossom's version. + (pthread_cond_wait): Replaced by John Bossom's version. + (pthread_cond_timedwait): Replaced by John Bossom's version. + (pthread_cond_signal): Replaced by John Bossom's version. + (pthread_cond_broadcast): Replaced by John Bossom's version. + +Thu Dec 17 19:10:46 1998 Ross Johnson + + * tsd.c (pthread_key_create): Replaced by John Bossom's version. + (pthread_key_delete): Replaced by John Bossom's version. + (pthread_setspecific): Replaced by John Bossom's version. + (pthread_getspecific): Replaced by John Bossom's version. Mon Dec 7 09:44:40 1998 Ross Johnson + * cancel.c (pthread_setcancelstate): Replaced by John Bossom's version. + (pthread_setcanceltype): Replaced by John Bossom's version. + (pthread_testcancel): Replaced by John Bossom's version. + (pthread_cancel): Replaced by John Bossom's version. + + * exit.c (pthread_exit): Replaced by John Bossom's version. + + * misc.c (pthread_self): Replaced by John Bossom's version. + (pthread_equal): Replaced by John Bossom's version. + + * sync.c (pthread_detach): Replaced by John Bossom's version. + (pthread_join): Replaced by John Bossom's version. + + * create.c (pthread_create): Replaced by John Bossom's version. + + * private.c (_pthread_processInitialize): New by John Bossom. + (_pthread_processTerminate): Non-public function by John Bossom. + (_pthread_threadStart): Non-public function by John Bossom. + (_pthread_threadDestroy): Non-public function by John Bossom. + (_pthread_cleanupStack): Non-public function by John Bossom. + (_pthread_tkAssocCreate): Non-public function by John Bossom. + (_pthread_tkAssocDestroy): Non-public function by John Bossom. + (_pthread_callUserDestroyRoutines): Non-public function by John Bossom. + + * implement.h: Added John Bossom's non-API structures and + declarations. + * dll.c (PthreadsEntryPoint): Cast return value of GetProcAddress to resolve compile warning from MSVC. + * dll.c (DLLmain): Replaced by John Bossom's version. + * dll.c (PthreadsEntryPoint): + Re-applied Anders Norlander's patch:- + Initialize _pthread_try_enter_critical_section at startup + and release kernel32 handle when DLL is being unloaded. + Sun Dec 6 21:54:35 1998 Ross Johnson * buildlib.bat: Fix args to CL when building the .DLL * cleanup.c (_pthread_destructor_run_all): Fix TSD key management. This is a tidy-up before TSD and Thread management is completely - replaced by John Bossom's much more elegant code. + replaced by John Bossom's code. * tsd.c (pthread_key_create): Fix TSD key management. diff --git a/MAINTAINERS b/MAINTAINERS index 3baf225..a09e2c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8,3 +8,5 @@ Ross Johnson rpj@ise.canberra.edu.au Active contributors Robert Colquhoun rjc@trump.net.au +John E. Bossom John.Bossom@cognos.com +Anders Norlander anorland@hem2.passagen.se diff --git a/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 . + */ + +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); +} + +/* */ + +#if 0 /* Pre Bossom */ + +#include + +#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 . + */ + +#include +#include + +#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); +} + +/* */ + + +#if 0 /* Pre Bossom */ + #include #include @@ -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 . + */ + +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 */ + +/* */ + + +#if 0 /* Pre Bossom */ + #include #include @@ -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 +#include +#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 -#include -#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 . + */ + +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 */ + +/* */ + + +#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 . + */ + +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 */ + +/* */ + + +#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 . + */ + +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 */ + + +/* */ + +#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 . + */ + +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 */ + +/* */ + + +#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 +#include -#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 +#include -} 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 . + */ + +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 */ + +/* */ + + +#if 0 /* Pre Bossom */ + #include /* 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 . + */ + +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)); +} + +/* */ + + +#if 0 /* Pre Bossom */ + #include #include "pthread.h" @@ -204,3 +510,4 @@ pthread_key_delete(pthread_key_t key) return ret; } +#endif /* Pre Bossom */ -- cgit v1.2.3