diff options
-rw-r--r-- | ChangeLog | 87 | ||||
-rw-r--r-- | dll.c | 13 | ||||
-rw-r--r-- | global.c | 7 | ||||
-rw-r--r-- | implement.h | 5 | ||||
-rw-r--r-- | mutex.c | 508 | ||||
-rw-r--r-- | private.c | 14 | ||||
-rw-r--r-- | pthread.h | 20 | ||||
-rw-r--r-- | tests/eyal1.c | 1 |
8 files changed, 613 insertions, 42 deletions
@@ -1,3 +1,18 @@ +Wed Jan 20 09:31:28 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * pthread.h (pthread_mutexattr_t): Changed to a pointer. + + * mutex.c (pthread_mutex_init): Conditionally create Win32 mutex + - from John Bossom's implementation. + (pthread_mutex_destroy): Conditionally close Win32 mutex + - from John Bossom's implementation. + (pthread_mutexattr_init): Replaced by John Bossom's version. + (pthread_mutexattr_destroy): Ditto. + (pthread_mutexattr_getpshared): New function from John Bossom's + implementation. + (pthread_mutexattr_setpshared): New function from John Bossom's + implementation. + Tue Jan 19 18:27:42 1999 Ross Johnson <rpj@swan.canberra.edu.au> * pthread.h (pthreadCancelableTimedWait): New prototype. @@ -16,6 +31,78 @@ Tue Jan 19 18:27:42 1999 Ross Johnson <rpj@swan.canberra.edu.au> pthreadCancelableTimedWait(). - Scott Lightner <scott@curriculum.com> +Tue Jan 19 10:27:39 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * pthread.h (pthread_mutexattr_setforcecs_np): New prototype. + + * mutex.c (pthread_mutexattr_init): Init 'pshared' and 'forcecs' + attributes to 0. + (pthread_mutexattr_setforcecs_np): New function (not portable). + + * pthread.h (pthread_mutex_t): + Add 'mutex' element. Set to NULL in PTHREAD_MUTEX_INITIALIZER. + The pthread_mutex_*() routines will try to optimise performance + by choosing either mutexes or critical sections as the basis + for pthread mutexes for each indevidual mutex. + (pthread_mutexattr_t_): Add 'forcecs' element. + Some applications may choose to force use of critical sections + if they know that:- + the mutex is PROCESS_PRIVATE and, + either the OS supports TryEnterCriticalSection() or + pthread_mutex_trylock() will never be called on the mutex. + This attribute will be setable via a non-portable routine. + + Note: We don't yet support PROCESS_SHARED mutexes, so the + implementation as it stands will default to Win32 mutexes only if + the OS doesn't support TryEnterCriticalSection. On Win9x, and early + versions of NT 'forcecs' will need to be set in order to get + critical section based mutexes. + +Sun Jan 17 12:01:26 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * pthread.h (PTHREAD_MUTEX_INITIALIZER): Init new 'staticinit' + value to '1' and existing 'valid' value to '1'. + + * global.c (_pthread_mutex_test_init_lock): Add. + + * implement.h (_pthread_mutex_test_init_lock.): Add extern. + + * private.c (_pthread_processInitialize): Init critical section for + global lock used by _mutex_check_need_init(). + (_pthread_processTerminate): Ditto (:s/Init/Destroy/). + + * dll.c (dllMain): Move call to FreeLibrary() so that it is only + called once when the process detaches. + + * mutex.c (_mutex_check_need_init): New static function to test + and init PTHREAD_MUTEX_INITIALIZER mutexes. Provides serialised + access to the internal state of the uninitialised static mutex. + Called from pthread_mutex_trylock() and pthread_mutex_lock() which + do a quick unguarded test to check if _mutex_check_need_init() + needs to be called. This is safe as the test is conservative + and is repeated inside the guarded section of + _mutex_check_need_init(). Thus in all calls except the first + calls to lock static mutexes, the additional overhead to lock any + mutex is a single memory fetch and test for zero. + + * pthread.h (pthread_mutex_t_): Add 'staticinit' member. Mutexes + initialised by PTHREAD_MUTEX_INITIALIZER aren't really initialised + until the first attempt to lock it. Using the 'valid' + flag (which flags the mutex as destroyed or not) to record this + information would be messy. It is possible for a statically + initialised mutex such as this to be destroyed before ever being + used. + + * mutex.c (pthread_mutex_trylock): Call _mutex_check_need_init() + to test/init PTHREAD_MUTEX_INITIALIZER mutexes. + (pthread_mutex_lock): Ditto. + (pthread_mutex_unlock): Add check to ensure we don't try to unlock + an unitialised static mutex. + (pthread_mutex_destroy): Add check to ensure we don't try to delete + a critical section that we never created. Allows us to destroy + a static mutex that has never been locked (and hence initialised). + (pthread_mutex_init): Set 'staticinit' flag to 0 for the new mutex. + Sun Jan 17 12:01:26 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> * private.c (_pthread_sem_timedwait): Move from semaphore.c. @@ -10,12 +10,17 @@ #include "implement.h" -/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */ +/* + * Function pointer to TryEnterCriticalSection if it exists; otherwise NULL + */ BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL; -/* Handle to kernel32.dll */ +/* + * Handle to kernel32.dll + */ static HINSTANCE _pthread_h_kernel32; + #ifdef _MSC_VER /* * lpvReserved yields an unreferenced formal parameter; @@ -89,11 +94,11 @@ DllMain ( * The DLL is being unmapped into the process's address space */ _pthread_processTerminate (); + + (void) FreeLibrary(_pthread_h_kernel32); } } - (void) FreeLibrary(_pthread_h_kernel32); - result = TRUE; } break; @@ -14,4 +14,11 @@ int _pthread_processInitialized = FALSE; pthread_key_t _pthread_selfThreadKey = NULL; pthread_key_t _pthread_cleanupKey = NULL; +/* + * Global lock for testing internal state of PTHREAD_MUTEX_INITIALIZER + * created mutexes. + */ +CRITICAL_SECTION _pthread_mutex_test_init_lock; + + diff --git a/implement.h b/implement.h index be71b7e..f897033 100644 --- a/implement.h +++ b/implement.h @@ -135,6 +135,7 @@ extern BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION); extern int _pthread_processInitialized; extern pthread_key_t _pthread_selfThreadKey; extern pthread_key_t _pthread_cleanupKey; +extern CRITICAL_SECTION _pthread_mutex_test_init_lock; #ifdef __cplusplus @@ -183,7 +184,8 @@ int _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime); */ #if defined(__CYGWIN32__) || defined(__CYGWIN__) -/* Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE +/* + * Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE * in order to avoid warnings because of return type */ @@ -206,3 +208,4 @@ int _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime); #endif /* _IMPLEMENT_H */ + @@ -10,85 +10,505 @@ #include "pthread.h" #include "implement.h" + +static int +_mutex_check_need_init(pthread_mutex_t *mutex) +{ + int result = 0; + + /* + * The following guarded test is specifically for statically + * initialised mutexes (via PTHREAD_MUTEX_INITIALIZER). + * + * Note that by not providing this synchronisation we risk + * introducing race conditions into applications which are + * correctly written. + * + * Approach + * -------- + * We know that static mutexes will not be PROCESS_SHARED + * so we can serialise access to internal state using + * Win32 Critical Sections rather than Win32 Mutexes. + * + * We still have a problem in that we would like a per-mutex + * lock, but attempting to create one will again lead + * to a race condition. We are forced to use a global + * lock in this instance. + * + * If using a single global lock slows applications down too much, + * multiple global locks could be created and hashed on some random + * value associated with each mutex, the pointer perhaps. At a guess, + * a good value for the optimal number of global locks might be + * the number of processors + 1. + * + * We need to maintain the following per-mutex state independently: + * - mutex staticinit (true iff static mutex and still + * needs to be initialised) + * - mutex valid (false iff mutex has been destroyed) + * + * For example, a mutex initialised by PTHREAD_MUTEX_INITIALIZER + * in this implementation will be valid but uninitialised until the thread + * attempts to lock it. It can also be destroyed (made invalid) before + * ever being locked. + */ + EnterCriticalSection(&_pthread_mutex_test_init_lock); + + /* + * We got here because staticinit tested true. + * Check staticinit again inside the critical section + * and only initialise if the mutex is valid (not been destroyed). + * If a static mutex has been destroyed, the application can + * re-initialise it only by calling pthread_mutex_init() + * explicitly. + */ + if (mutex->staticinit && mutex->valid) + { + result = pthread_mutex_init(mutex, NULL); + } + + LeaveCriticalSection(&_pthread_mutex_test_init_lock); + + return(result); +} + int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { + int result = 0; + if (mutex == NULL) { return EINVAL; } - /* Create a critical section. */ - InitializeCriticalSection(&mutex->cs); + mutex->mutex = 0; - /* Mark as valid. */ - mutex->valid = 1; + /* + * Assuming any race condition here is harmless. + */ + if (mutex->valid && !mutex->staticinit) + { + return EBUSY; + } - return 0; + if (attr != NULL + && *attr != NULL + && (*attr)->pshared == PTHREAD_PROCESS_SHARED + ) + { + /* + * Creating mutex that can be shared between + * processes. + */ +#if _POSIX_THREAD_PROCESS_SHARED + + /* + * Not implemented yet. + */ + +#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet. + + mutex->mutex = CreateMutex ( + NULL, + FALSE, + ????); + result = (mutex->mutex == 0) ? EAGAIN : 0; + +#else + + result = ENOSYS; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ + } + else + { + if (_pthread_try_enter_critical_section != NULL + || (attr != NULL + && *attr != NULL + && (*attr)->forcecs == 1) + ) + { + /* + * Create a critical section. + */ + InitializeCriticalSection(&mutex->cs); + } + else + { + /* + * Create a mutex that can only be used within the + * current process + */ + mutex->mutex = CreateMutex (NULL, + FALSE, + NULL); + result = (mutex->mutex == 0) ? EAGAIN : 0; + } + } + + if (result == 0) + { + mutex->staticinit = 0; + + /* Mark as valid. */ + mutex->valid = 1; + } + + return(result); } int pthread_mutex_destroy(pthread_mutex_t *mutex) { + int result = 0; + if (mutex == NULL) { return EINVAL; } - DeleteCriticalSection(&mutex->cs); - - /* Mark as invalid. */ - mutex->valid = 0; + /* + * Check to see if we have something to delete. + */ + if (!mutex->staticinit) + { + if (mutex->mutex == 0) + { + DeleteCriticalSection(&mutex->cs); + } + else + { + result = (CloseHandle (mutex->mutex) ? 0 : EINVAL); + } + } - return 0; + if (result == 0) + { + mutex->mutex = 0; + + /* Mark as invalid. */ + mutex->valid = 0; + } + + return(result); } int -pthread_mutexattr_init(pthread_mutexattr_t *attr) +pthread_mutexattr_init (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Initializes a mutex attributes object with default + * attributes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Initializes a mutex attributes object with default + * attributes. + * + * NOTES: + * 1) Used to define mutex types + * + * RESULTS + * 0 successfully initialized attr, + * ENOMEM insufficient memory for attr. + * + * ------------------------------------------------------ + */ +{ + pthread_mutexattr_t attr_result; + int result = 0; + + attr_result = calloc (1, sizeof (*attr_result)); + + result = (attr_result == NULL) + ? ENOMEM + : 0; + + *attr = attr_result; + + return (result); + +} /* pthread_mutexattr_init */ + + +int +pthread_mutexattr_destroy (pthread_mutexattr_t * attr) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_t + * + * + * DESCRIPTION + * Destroys a mutex attributes object. The object can + * no longer be used. + * + * NOTES: + * 1) Does not affect mutexes 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_mutexattr_destroy */ + + +int +pthread_mutexattr_setforcecs_np(pthread_mutexattr_t *attr, + int forcecs) { - if (attr == NULL) + if (attr == NULL || *attr == NULL) { /* This is disallowed. */ return EINVAL; } - /* None of the optional attributes are supported yet. */ + (*attr)->forcecs = forcecs; + return 0; } + int -pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, + int *pshared) + /* + * ------------------------------------------------------ + * DOCPUBLIC + * Determine whether mutexes created with 'attr' can be + * shared between processes. + * + * PARAMETERS + * attr + * pointer to an instance of pthread_mutexattr_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 + * 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 retrieved attribute, + * EINVAL 'attr' is invalid, + * + * ------------------------------------------------------ + */ { - /* Nothing to do. */ - return 0; -} + int result; + + if ((attr != NULL && *attr != NULL) && + (pshared != NULL)) + { + *pshared = (*attr)->pshared; + result = 0; + } + else + { + *pshared = PTHREAD_PROCESS_PRIVATE; + result = EINVAL; + } + + return (result); + +} /* pthread_mutexattr_getpshared */ + + +int +pthread_mutexattr_setpshared (pthread_mutexattr_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_mutexattr_setpshared */ + int pthread_mutex_lock(pthread_mutex_t *mutex) { - if (!mutex->valid) + int result = 0; + + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check 'staticinit' + * again inside the guarded section of _mutex_check_need_init() + * to avoid race conditions. + */ + if (mutex->staticinit) { - pthread_mutex_init(mutex, NULL); + result = _mutex_check_need_init(mutex); } - EnterCriticalSection(&mutex->cs); - return 0; + + if (result == 0) + { + if (mutex->mutex == 0) + { + EnterCriticalSection(&mutex->cs); + } + else + { + result = (WaitForSingleObject(mutex->mutex, INFINITE) + == WAIT_OBJECT_0) + ? 0 + : EINVAL; + } + } + + return(result); } int pthread_mutex_unlock(pthread_mutex_t *mutex) { - if (!mutex->valid) + int result = 0; + + /* + * If the thread calling us holds the mutex then there is no + * race condition. If another thread holds the + * lock then we shouldn't be in here. + */ + if (!mutex->staticinit && mutex->valid) { - return EINVAL; + if (mutex->mutex == 0) + { + LeaveCriticalSection(&mutex->cs); + } + else + { + result = (ReleaseMutex (mutex->mutex) ? 0 : EINVAL); + } } - LeaveCriticalSection(&mutex->cs); - return 0; + else + { + result = EINVAL; + } + + return(result); } int pthread_mutex_trylock(pthread_mutex_t *mutex) { - if (_pthread_try_enter_critical_section == NULL) + int result = 0; + + if (mutex->mutex == 0 && _pthread_try_enter_critical_section == NULL) { /* TryEnterCriticalSection does not exist in the OS; return ENOSYS. */ return ENOSYS; @@ -99,10 +519,40 @@ pthread_mutex_trylock(pthread_mutex_t *mutex) return EINVAL; } - if (!mutex->valid) + /* + * We do a quick check to see if we need to do more work + * to initialise a static mutex. We check 'staticinit' + * again inside the guarded section of _mutex_check_need_init() + * to avoid race conditions. + */ + if (mutex->staticinit) + { + result = _mutex_check_need_init(mutex); + } + + if (result == 0) { - pthread_mutex_init(mutex, NULL); + if (mutex->mutex == 0) + { + if ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) + { + result = EBUSY; + } + } + else + { + DWORD status; + + status = WaitForSingleObject (mutex->mutex, 0); + + if (status != WAIT_OBJECT_0) + { + result = ((status == WAIT_TIMEOUT) + ? EBUSY + : EINVAL); + } + } } - return ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) ? EBUSY : 0; + return(result); } @@ -9,6 +9,8 @@ #include "pthread.h" #include "implement.h" +#include <sys/timeb.h> + /* * Code contributed by John E. Bossom <JEB>. */ @@ -49,6 +51,11 @@ _pthread_processInitialize (void) _pthread_processTerminate (); } + /* + * Set up the global mutex test and init check lock. + */ + InitializeCriticalSection(&_pthread_mutex_test_init_lock); + return (_pthread_processInitialized); } /* processInitialize */ @@ -99,6 +106,11 @@ _pthread_processTerminate (void) _pthread_cleanupKey = NULL; } + /* + * Destroy up the global mutex test and init check lock. + */ + DeleteCriticalSection(&_pthread_mutex_test_init_lock); + _pthread_processInitialized = FALSE; } @@ -483,7 +495,7 @@ _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime) if (abstime == NULL) { - msecs = INFINITE; + milliseconds = INFINITE; } else { @@ -415,7 +415,7 @@ extern "C" 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_mutexattr_t_ *pthread_mutexattr_t; typedef struct pthread_cond_t_ *pthread_cond_t; typedef struct pthread_condattr_t_ *pthread_condattr_t; @@ -460,10 +460,10 @@ extern "C" * ==================== */ -/* 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 } +#define PTHREAD_MUTEX_INITIALIZER { 1, 1, 0, NULL } /* @@ -569,13 +569,16 @@ struct pthread_attr_t_ { struct pthread_mutex_t_ { - int valid; - CRITICAL_SECTION cs; - }; + int staticinit; + int valid; + HANDLE mutex; + CRITICAL_SECTION cs; +}; struct pthread_mutexattr_t_ { int pshared; + int forcecs; }; @@ -838,6 +841,9 @@ int pthread_mutexattr_getpshared (const pthread_mutexattr_t int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, int pshared); +int pthread_mutexattr_setforcecs_np(pthread_mutexattr_t *attr, + int forcecs); + /* * Mutex Functions */ diff --git a/tests/eyal1.c b/tests/eyal1.c index e32d29e..f51bc9c 100644 --- a/tests/eyal1.c +++ b/tests/eyal1.c @@ -161,6 +161,7 @@ print_server (void *ptr) */ if (pthread_mutex_lock (&mutex_todo)) return (-6); + mywork = todo; if (todo >= 0) { ++todo; |