From 1648c7a97f27d10ad302c6141562ece01065e1d7 Mon Sep 17 00:00:00 2001 From: rpj Date: Fri, 9 Feb 2001 06:51:30 +0000 Subject: Remodeled mutex routines again to eliminate critical sections. --- ANNOUNCE | 43 ++++-- ChangeLog | 21 +++ README.NONPORTABLE | 31 +++++ global.c | 6 + implement.h | 34 +---- mutex.c | 399 ++++++++++++----------------------------------------- nonportable.c | 56 ++++++++ pthread.def | 3 +- pthread.h | 18 ++- 9 files changed, 252 insertions(+), 359 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index 8ba2989..d2def8b 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -55,24 +55,47 @@ New: be different to some other implementations, eg Solaris. - PTHREAD_MUTEX_NORMAL will simulate thread deadlock + PTHREAD_MUTEX_NORMAL will cause thread deadlock if the owner of a mutex tries to relock it without - first unlocking it, however the lock will be released - if the owner thread is async-canceled. + first unlocking it. It has slightly less overhead. -- Pthreads-win32 mutexes are now always based on - Win32 critical sections. We no longer use Win32 - mutexes when TryEnterCriticalSection isn't - supported. + The behaviour of PTHREAD_MUTEX_DEFAULT can be + remapped to any other type through the new + non-portable function + + pthread_mutex_setdefaulttype_np() + + (see README.NONPORTABLE) + + Remapping only effects newly initialised mutexes. + So if you want behaviour more like Solaris + POSIX mutexes, which are non-recursive by default, + you can call this at the start of your application: + + int previousType; + pthread_mutex_setdefaulttype_np(PTHREAD_MUTEX_ERRORCHECK, + &previousType); + +- Pthreads-win32 mutexes are now based on Win32 + critical sections for all Windows versions. We no longer + depend on TryEnterCriticalSection. - Thomas Pfaff + This may change again before the next snapshot + to rely solely on Win32's Interlocked* routines. + Bugs fixed: +- Pthread_mutex_trylock() now properly returns EBUSY + even when the current thread owns the mutex. + Consequently, pthread_mutex_destroy() will no longer + destroy a locked mutex (it will return EBUSY). + - Thomas Pfaff + - The condition variable and read-write lock functions - have been improved. For details re the [fixed] problems - in the CV implementation see the file README.CV + have been improved. For discussion re the [fixed] + problems in the CV implementation see the file README.CV - Alexander Terekhov - Known bugs in this snapshot --------------------------- diff --git a/ChangeLog b/ChangeLog index 71c7f2f..5a8c6a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2001-02-09 Ross Johnson + + * nonportable.c (pthread_mutex_setdefaulttype_np): New + function for changing the default mutex type. + + * mutex.c (ptw32_InitializeCriticalSection): Removed. + (ptw32_InitializeCriticalSection): Removed. + (ptw32_InitializeCriticalSection): Removed. + (ptw32_InitializeCriticalSection): Removed. + (ptw32_InitializeCriticalSection): Removed. + (pthread_mutex_init): Apply Thomas Pfaff's original + patches but altered slightly to avoid using + critical sections and retain/adapt for different + mutex types (see log entry for 2001-01-10). + (pthread_mutex_destroy): Likewise. + (pthread_mutex_lock): Likewise. + (pthread_mutex_unlock): Likewise. + (pthread_mutex_trylock): Likewise. + + * Tagged repository 'exp-2001-02-09-passed'. + 2001-02-09 Ross Johnson * sched.c (pthread_setconcurrency): Moved to misc.c. diff --git a/README.NONPORTABLE b/README.NONPORTABLE index 7dfba7f..8319853 100644 --- a/README.NONPORTABLE +++ b/README.NONPORTABLE @@ -40,6 +40,37 @@ pthread_delay_np (const struct timespec *interval); 0 Successful completion. [EINVAL] The value specified by interval is invalid. +int +pthread_mutex_setdefaulttype_np (int newtype, int * oldtype); + + The routine sets the default type to be given to all + POSIX mutexes initialised after the function + is called. Any of the following type values + can be made the default type: + + PTHREAD_MUTEX_NORMAL + PTHREAD_MUTEX_ERRORCHECK + PTHREAD_MUTEX_RECURSIVE + PTHREAD_MUTEX_DEFAULT + + Any mutex initialised with type PTHREAD_MUTEX_DEFAULT + will be set to the mapped type instead. Previously + initialised mutexes are not changed. + + When set to PTHREAD_MUTEX_DEFAULT (the initial + value), mutexes will behave as for the + PTHREAD_MUTEX_RECURSIVE type. + + If 'oldtype' is a non-NULL pointer, the previous type is + returned through it. + To get the previous type without setting a new type, + use -1 as the 'newtype' value. + + Return Values + + 0 Successfully changed to new type. + [EINVAL] New type isn't valid. + BOOL pthread_win32_process_attach_np (void); diff --git a/global.c b/global.c index 128a17b..9278f07 100644 --- a/global.c +++ b/global.c @@ -54,3 +54,9 @@ CRITICAL_SECTION ptw32_cond_test_init_lock; * created read/write locks. */ CRITICAL_SECTION ptw32_rwlock_test_init_lock; + +/* + * The default mutex type can be remapped by teh application + * via the pthread_mutex_setdefaulttype_np() function. + */ +int ptw32_mutex_mapped_default = PTHREAD_MUTEX_DEFAULT; diff --git a/implement.h b/implement.h index 165708a..a1a52f8 100644 --- a/implement.h +++ b/implement.h @@ -121,33 +121,17 @@ struct pthread_attr_t_ { #define PTW32_OBJECT_AUTO_INIT ((void *) -1) #define PTW32_OBJECT_INVALID NULL -/* - * Our own critical section type, used - * when the system doesn't support TryEnterCriticalSection() - */ -typedef struct ptw32_cs_t_ { - int valid; - pthread_t owner; - long lock_idx; - long entered_count; -} ptw32_cs_t; - -typedef union ptw32_cs_u_t_ { - CRITICAL_SECTION cs; - ptw32_cs_t csFake; -} ptw32_cs_u_t; - struct pthread_mutexattr_t_ { int pshared; int type; }; struct pthread_mutex_t_ { - ptw32_cs_u_t cs; - int lockCount; + int lock_idx; + int try_lock; int pshared; int type; - pthread_t ownerThread; + pthread_t owner; }; struct pthread_key_t_ { @@ -324,7 +308,7 @@ extern CRITICAL_SECTION ptw32_mutex_test_init_lock; extern CRITICAL_SECTION ptw32_cond_test_init_lock; extern CRITICAL_SECTION ptw32_rwlock_test_init_lock; extern BOOL (WINAPI *ptw32_try_enter_critical_section)(LPCRITICAL_SECTION); - +extern int ptw32_mutex_mapped_default; /* Declared in misc.c */ #ifdef NEED_CALLOC @@ -380,16 +364,6 @@ BOOL ptw32_increase_semaphore(sem_t * sem, unsigned int n); #endif /* NEED_SEM */ -int ptw32_InitializeCriticalSection (ptw32_cs_u_t *); - -void ptw32_DeleteCriticalSection (ptw32_cs_u_t *); - -void ptw32_EnterCriticalSection (ptw32_cs_u_t *); - -void ptw32_LeaveCriticalSection (ptw32_cs_u_t *); - -BOOL ptw32_TryEnterCriticalSection (ptw32_cs_u_t *); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mutex.c b/mutex.c index f43286c..f99ce86 100644 --- a/mutex.c +++ b/mutex.c @@ -85,252 +85,6 @@ ptw32_mutex_check_need_init(pthread_mutex_t *mutex) } -/* - * The following internal versions of *CriticalSection() - * include an implementation of TryEnterCriticalSection - * for platforms on which that function has not been - * provided by Microsoft (eg. W95/98). This allows us - * to avoid using Win32 mutexes as the basis - * of our implementation of POSIX mutex locks. - * - * Where TryEnterCriticalSection() is provided by the - * platform, these routines act as wrappers with - * minimal additional overhead. Otherwise, these - * routines manage additional state in order to - * properly emulate TryEnterCriticalSection(). - */ - -int -ptw32_InitializeCriticalSection (ptw32_cs_u_t * csect) - /* - * ------------------------------------------------------ - * - * PARAMETERS - * csect - * pointer to an instance of ptw32_cs_u_t - * csect->csFake must be NULL on entry. - * - * DESCRIPTION - * Internal implementation of InitializeCriticalSection. - * - * RETURN - * 0 Initialisation successful - * EINVAL csFake already initialised - * - * ------------------------------------------------------ - */ -{ - int result = 0; - - if (NULL != ptw32_try_enter_critical_section) - { - InitializeCriticalSection(&csect->cs); - } - else - { - while (InterlockedIncrement(&csect->csFake.lock_idx) > 0) - { - InterlockedDecrement(&csect->csFake.lock_idx); - Sleep(0); - } - - if (csect->csFake.valid) - { - result = EINVAL; - } - else - { - csect->csFake.owner = NULL; - csect->csFake.entered_count = 0; - csect->csFake.valid = 1; - } - - InterlockedDecrement(&csect->csFake.lock_idx); - } - - return result; -} - -void -ptw32_DeleteCriticalSection (ptw32_cs_u_t * csect) - /* - * ------------------------------------------------------ - * - * PARAMETERS - * csect - * pointer to an instance of ptw32_cs_u_t - * - * DESCRIPTION - * Internal implementation of DeleteCriticalSection. - * - * ------------------------------------------------------ - */ -{ - if (NULL != ptw32_try_enter_critical_section) - { - DeleteCriticalSection(&csect->cs); - } - else - { - while (InterlockedIncrement(&csect->csFake.lock_idx) > 0) - { - InterlockedDecrement(&csect->csFake.lock_idx); - Sleep(0); - } - - if (csect->csFake.valid - && csect->csFake.entered_count == 0) - { - csect->csFake.valid = 0; - } - - InterlockedDecrement(&csect->csFake.lock_idx); - } -} - -void -ptw32_EnterCriticalSection(ptw32_cs_u_t * csect) - /* - * ------------------------------------------------------ - * - * PARAMETERS - * csect - * pointer to an instance of ptw32_cs_u_t - * - * DESCRIPTION - * Internal implementation of EnterCriticalSection. - * - * ------------------------------------------------------ - */ -{ - if (NULL != ptw32_try_enter_critical_section) - { - EnterCriticalSection(&csect->cs); - } - else - { - pthread_t self = pthread_self(); - - while (InterlockedIncrement(&csect->csFake.lock_idx) >= 0 - && csect->csFake.valid - && csect->csFake.owner != NULL - && csect->csFake.owner != self) - { - InterlockedDecrement(&csect->csFake.lock_idx); - Sleep(0); - } - - if (csect->csFake.valid) - { - csect->csFake.entered_count++; - csect->csFake.owner = self; - } - - InterlockedDecrement(&csect->csFake.lock_idx); - } -} - -void -ptw32_LeaveCriticalSection (ptw32_cs_u_t * csect) - /* - * ------------------------------------------------------ - * - * PARAMETERS - * csect - * pointer to an instance of ptw32_cs_u_t - * - * DESCRIPTION - * Internal implementation of LeaveCriticalSection. - * - * ------------------------------------------------------ - */ -{ - if (NULL != ptw32_try_enter_critical_section) - { - LeaveCriticalSection(&csect->cs); - } - else - { - while (InterlockedIncrement(&csect->csFake.lock_idx) > 0) - { - InterlockedDecrement(&csect->csFake.lock_idx); - Sleep(0); - } - - if (csect->csFake.valid) - { - csect->csFake.entered_count--; - if (csect->csFake.entered_count == 0) - { - csect->csFake.owner = NULL; - } - } - - InterlockedDecrement(&csect->csFake.lock_idx); - } -} - -BOOL -ptw32_TryEnterCriticalSection (ptw32_cs_u_t * csect) - /* - * ------------------------------------------------------ - * - * PARAMETERS - * csect - * pointer to an instance of ptw32_cs_u_t - * - * DESCRIPTION - * Internal implementation of TryEnterCriticalSection. - * - * RETURNS - * FALSE Current thread doesn't own the - * lock, - * TRUE Current thread owns the lock - * (if the current thread already - * held the lock then we recursively - * enter). - * ------------------------------------------------------ - */ -{ - BOOL result = FALSE; - - if (NULL != ptw32_try_enter_critical_section) - { - result = (*ptw32_try_enter_critical_section)(&csect->cs); - } - else - { - pthread_t self; - - while (InterlockedIncrement(&csect->csFake.lock_idx) > 0) - { - InterlockedDecrement(&csect->csFake.lock_idx); - Sleep(0); - } - - self = pthread_self(); - - if (csect->csFake.valid - && (csect->csFake.owner == NULL || csect->csFake.owner == self)) - { - /* - * The semantics of TryEnterCriticalSection - * (according to the documentation at MS) - * are that the CS is entered recursively - * if the thread is the current owner. - */ - csect->csFake.entered_count++; - csect->csFake.owner = self; - result = TRUE; - } - - InterlockedDecrement(&csect->csFake.lock_idx); - } - - return result; -} - - int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { @@ -349,16 +103,9 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) goto FAIL0; } - mx->cs.csFake.lock_idx = -1; - - result = ptw32_InitializeCriticalSection(&mx->cs); - if (result != 0) - { - goto FAIL1; - } - - mx->lockCount = 0; - mx->ownerThread = NULL; + mx->lock_idx = -1; + mx->owner = NULL; + mx->try_lock = 0; if (attr != NULL && *attr != NULL) { @@ -393,7 +140,11 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) mx->pshared = PTHREAD_PROCESS_PRIVATE; } -FAIL1: + if (mx->type == PTHREAD_MUTEX_DEFAULT) + { + mx->type = ptw32_mutex_mapped_default; + } + if (result != 0 && mx != NULL) { free(mx); @@ -428,7 +179,7 @@ pthread_mutex_destroy(pthread_mutex_t *mutex) * Check to see if the mutex is held by any thread. We * can't destroy it if it is. Pthread_mutex_trylock is * not recursive and will return EBUSY even if the current - * thread (us) holds the lock. + * thread holds the lock. */ result = pthread_mutex_trylock(&mx); @@ -447,7 +198,6 @@ pthread_mutex_destroy(pthread_mutex_t *mutex) if (result == 0) { - ptw32_DeleteCriticalSection(&mx->cs); free(mx); } else @@ -892,53 +642,66 @@ pthread_mutex_lock(pthread_mutex_t *mutex) switch (mx->type) { - case PTHREAD_MUTEX_NORMAL: - if (pthread_equal(mx->ownerThread, self)) + case PTHREAD_MUTEX_DEFAULT: + case PTHREAD_MUTEX_RECURSIVE: + if (InterlockedIncrement(&mx->lock_idx) > 0) { - /* - * Pretend to be deadlocked but release the - * mutex if we are [asynchronously] canceled. - */ - pthread_cleanup_push(pthread_mutex_unlock, (void *) mutex); - while (TRUE) + while (mx->try_lock) { Sleep(0); } - pthread_cleanup_pop(1); - /* - * Never gets beyond here. - */ + while (mx->lock_idx > 0 && mx->owner != self) + { + Sleep(0); + } } - else + mx->owner = self; + break; + case PTHREAD_MUTEX_NORMAL: + /* + * If the thread already owns the mutex + * then the thread will become deadlocked. + */ + while (InterlockedIncrement(&mx->lock_idx) > 0) { - ptw32_EnterCriticalSection(&mx->cs); + InterlockedDecrement(&mx->lock_idx); + Sleep(0); } + mx->owner = self; break; case PTHREAD_MUTEX_ERRORCHECK: - if (pthread_equal(mx->ownerThread, self)) + if (0 == InterlockedIncrement(&mx->lock_idx)) { - result = EDEADLK; + mx-owner = self; } else { - ptw32_EnterCriticalSection(&mx->cs); + while (mx->try_lock) + { + Sleep(0); + } + + while (mx->lock_idx > 0 && mx->owner != self) + { + Sleep(0); + } + + if (mx->owner == self) + { + InterlockedDecrement(&mx->lock_idx); + result = EDEADLK; + } + else + { + mx->owner = self; + } } break; - case PTHREAD_MUTEX_DEFAULT: - case PTHREAD_MUTEX_RECURSIVE: - ptw32_EnterCriticalSection(&mx->cs); - break; default: result = EINVAL; break; } - - if (result == 0) - { - mx->ownerThread = self; - mx->lockCount++; - } } return result; @@ -964,17 +727,25 @@ pthread_mutex_unlock(pthread_mutex_t *mutex) */ if (mx != (pthread_mutex_t) PTW32_OBJECT_AUTO_INIT) { - if (pthread_equal(mx->ownerThread, pthread_self())) + if (mx->owner == pthread_self()) { - mx->lockCount--; - - if (mx->lockCount == 0) - { - mx->ownerThread = NULL; - } + switch (mx->type) + { + case PTHREAD_MUTEX_NORMAL: + case PTHREAD_MUTEX_ERRORCHECK: + mx->owner = NULL; + break; + case PTHREAD_MUTEX_RECURSIVE: + default: + if (mx->lock_idx == 0) + { + mx->owner = NULL; + } + break; + } - ptw32_LeaveCriticalSection(&mx->cs); - } + InterlockedDecrement(&mx->lock_idx); + } else { result = EPERM; @@ -1012,28 +783,34 @@ pthread_mutex_trylock(pthread_mutex_t *mutex) } mx = *mutex; - self = pthread_self(); if (result == 0) { + self = pthread_self(); /* - * TryEnterCriticalSection is a little different to - * the POSIX trylock semantics. Trylock returns - * EBUSY even if the calling thread already owns - * the mutex - it doesn't lock it recursively, even + * Trylock returns EBUSY if the mutex is held already, + * even if the current thread owns the mutex - ie. it + * doesn't lock it recursively, even * if the mutex type is PTHREAD_MUTEX_RECURSIVE. */ - if (ptw32_TryEnterCriticalSection(&mx->cs)) + if (0 == (mx->lock_idx + 1)) { - /* - * We now own the lock, but check that we don't - * already own the mutex. - */ - if (pthread_equal(mx->ownerThread, self)) + mx->try_lock++; + + if (0 == InterlockedIncrement(&mx->lock_idx)) { - ptw32_LeaveCriticalSection(&mx->cs); - result = EBUSY; + mx->owner = self; + } + else + { + InterlockedDecrement(&mx->lock_idx); + if (mx->owner == self) + { + result = EBUSY; + } } + + mx->try_lock--; } else { @@ -1041,12 +818,6 @@ pthread_mutex_trylock(pthread_mutex_t *mutex) } } - if (result == 0) - { - mx->ownerThread = self; - mx->lockCount++; - } - return result; } diff --git a/nonportable.c b/nonportable.c index 17cbc60..27b67df 100644 --- a/nonportable.c +++ b/nonportable.c @@ -248,3 +248,59 @@ pthread_win32_thread_detach_np () return TRUE; } + + +/* + * pthread_mutex_setdefaulttype_np -- + * + * Sets the default type to be given to all + * POSIX mutexes initialised after the function + * is called. Any of the following type values + * can be made the default type: + * + * PTHREAD_MUTEX_NORMAL + * PTHREAD_MUTEX_ERRORCHECK + * PTHREAD_MUTEX_RECURSIVE + * PTHREAD_MUTEX_DEFAULT + * + * Any mutex initialised with type PTHREAD_MUTEX_DEFAULT + * will be set to the mapped type instead. Previously + * initialised mutexes are not changed. + * + * When set to PTHREAD_MUTEX_DEFAULT (the initial + * value), mutexes will behave as for the + * PTHREAD_MUTEX_RECURSIVE type. + * + * If 'oldtype' is a non-NULL pointer, the previous type is + * returned through it. + * To get the previous type without setting a new type, + * use -1 as the 'newtype' value. Of course, the following: + * + * pthread_mutex_setdefaulttype_np(-1, NULL); + * + * is then an extravagant equivalent to the value 0 (zero). + */ +int +pthread_mutex_setdefaulttype_np (int newtype, int * oldtype) +{ + int result = 0; + + if (oldtype != NULL) + { + *oldType = ptw32_mutex_mapped_default; + } + + switch (newtype) + { + case PTHREAD_MUTEX_DEFAULT: + case PTHREAD_MUTEX_NORMAL: + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_RECURSIVE: + ptw32_mutex_mapped_default = newtype; + break; + default: + result = EINVAL; + } + + return result; +} diff --git a/pthread.def b/pthread.def index 580caf5..b16aba7 100644 --- a/pthread.def +++ b/pthread.def @@ -1,5 +1,5 @@ ; pthread.def -; Last updated: $Date: 2001/02/06 05:44:38 $ +; Last updated: $Date: 2001/02/09 06:51:30 $ ; Currently unimplemented functions are commented out. @@ -107,6 +107,7 @@ pthread_rwlock_unlock ; pthread_getw32threadhandle_np pthread_delay_np +pthread_mutex_setdefaulttype_np pthreadCancelableWait pthreadCancelableTimedWait ; For use when linking statically diff --git a/pthread.h b/pthread.h index d12bc41..e40aaef 100644 --- a/pthread.h +++ b/pthread.h @@ -631,10 +631,10 @@ int pthread_attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_setscope (pthread_attr_t *, - int); + int); int pthread_attr_getscope (const pthread_attr_t *, - int *); + int *); /* * PThread Functions @@ -793,9 +793,18 @@ int pthread_rwlock_unlock(pthread_rwlock_t *lock); * Non-portable functions */ -/* Possibly supported by other POSIX threads implimentations */ +/* + * Possibly supported by other POSIX threads implimentations + */ int pthread_delay_np (struct timespec * interval); +/* + * Remaps the default mutex type to any of the + * other possible types. Returns the previous type. + */ +int pthread_mutex_setdefaulttype_np (int newtype, + int * oldtype); + /* * Returns the Win32 thread HANDLE associated * with the given POSIX thread. @@ -828,7 +837,8 @@ int pthread_win32_thread_detach_np(void); * WaitForMultipleObjects. */ int pthreadCancelableWait (HANDLE waitHandle); -int pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout); +int pthreadCancelableTimedWait (HANDLE waitHandle, + DWORD timeout); /* * Thread-Safe C Runtime Library Mappings. -- cgit v1.2.3