From b0cf9efa6afeb8a7dbddf124dae173a2d633c801 Mon Sep 17 00:00:00 2001 From: rpj Date: Fri, 1 Oct 2004 07:17:09 +0000 Subject: Mutex speedups --- ChangeLog | 42 +++++--- implement.h | 7 -- pthread_mutex_destroy.c | 9 +- pthread_mutex_init.c | 2 +- pthread_mutex_lock.c | 64 ++++++------ pthread_mutex_timedlock.c | 244 ++++++++++++++++++++++------------------------ pthread_mutex_trylock.c | 47 +++++---- pthread_mutex_unlock.c | 72 ++++++++++---- tests/GNUmakefile | 2 +- tests/benchlib.c | 25 +++++ tests/benchtest.h | 3 + tests/benchtest1.c | 42 +++++++- tests/mutex4.c | 6 ++ 13 files changed, 341 insertions(+), 224 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7ce169c..9a3cd4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,23 +1,39 @@ +2004-09-27 Ross Johnson + + * pthread_mutex_lock.c (pthread_mutex_lock): Separate PTHREAD_MUTEX_NORMAL + logic since we do not need to keep or check some state required by other + mutex types; do not check mutex pointer arg for validity - leave this to + the system since we are only checking for NULL pointers. This should improve + speed of NORMAL mutexes and marginally improve speed of other type. + * pthread_mutex_trylock.c (pthread_mutex_trylock): Likewise. + * pthread_mutex_unlock.c (pthread_mutex_unlock): Likewise; also avoid + entering the critical section for the no-waiters case, with approx. 30% + reduction in lock/unlock overhead for this case.. + * pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise; also + no longer keeps mutex if post-timeout second attempt succeeds - this will + assist applications that wish to impose strict lock deadlines, rather than + simply to escape from frozen locks. + 2004-09-09 Tristan Savatier - * pthread.h (struct pthread_once_t_): Qualify the 'done' element - as 'volatile'. - * pthread_once.c: Concerned about possible race condition, - specifically on MPU systems re concurrent access to multibyte types. - [Maintainer's note: the race condition is harmless on SPU systems - and only a problem on MPU systems if concurrent access results in an - exception (presumably generated by a hardware interrupt). There are - other instances of similar harmless race conditions that have not been - identified as issues.] + * pthread.h (struct pthread_once_t_): Qualify the 'done' element + as 'volatile'. + * pthread_once.c: Concerned about possible race condition, + specifically on MPU systems re concurrent access to multibyte types. + [Maintainer's note: the race condition is harmless on SPU systems + and only a problem on MPU systems if concurrent access results in an + exception (presumably generated by a hardware interrupt). There are + other instances of similar harmless race conditions that have not been + identified as issues.] 2004-09-09 Ross Johnson - * pthread.h: Declare additional types as volatile. + * pthread.h: Declare additional types as volatile. 2004-08-27 Ross Johnson - * pthread_barrier_wait.c (pthread_barrier_wait): Remove excessive code - by substituting the internal non-cancelable version of sem_wait - (ptw32_semwait). + * pthread_barrier_wait.c (pthread_barrier_wait): Remove excessive code + by substituting the internal non-cancelable version of sem_wait + (ptw32_semwait). 2004-08-25 Ross Johnson diff --git a/implement.h b/implement.h index 95308f5..400598c 100644 --- a/implement.h +++ b/implement.h @@ -423,13 +423,6 @@ struct ThreadKeyAssoc #define PTW32_EPS_EXIT (1) #define PTW32_EPS_CANCEL (2) -/* Mutex constants */ -enum -{ - PTW32_MUTEX_LOCK_IDX_INIT = -1, - PTW32_MUTEX_OWNER_ANONYMOUS = 1 -}; - /* Useful macros */ #define PTW32_MAX(a,b) ((a)<(b)?(b):(a)) diff --git a/pthread_mutex_destroy.c b/pthread_mutex_destroy.c index a777598..c2289d0 100644 --- a/pthread_mutex_destroy.c +++ b/pthread_mutex_destroy.c @@ -44,10 +44,9 @@ pthread_mutex_destroy (pthread_mutex_t * mutex) int result = 0; pthread_mutex_t mx; - if (mutex == NULL || *mutex == NULL) - { - return EINVAL; - } + /* + * Let the system deal with invalid pointers. + */ /* * Check to see if we have something to delete. @@ -64,7 +63,7 @@ pthread_mutex_destroy (pthread_mutex_t * mutex) */ if (result == 0) { - if (1 == mx->recursive_count) + if (mx->kind != PTHREAD_MUTEX_RECURSIVE || 1 == mx->recursive_count) { /* * FIXME!!! diff --git a/pthread_mutex_init.c b/pthread_mutex_init.c index ebf26d5..93504ad 100644 --- a/pthread_mutex_init.c +++ b/pthread_mutex_init.c @@ -80,7 +80,7 @@ pthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr) } else { - mx->lock_idx = PTW32_MUTEX_LOCK_IDX_INIT; + mx->lock_idx = -1; mx->recursive_count = 0; mx->kind = (attr == NULL || *attr == NULL ? PTHREAD_MUTEX_DEFAULT : (*attr)->kind); diff --git a/pthread_mutex_lock.c b/pthread_mutex_lock.c index f92b39b..b733801 100644 --- a/pthread_mutex_lock.c +++ b/pthread_mutex_lock.c @@ -46,11 +46,9 @@ pthread_mutex_lock (pthread_mutex_t * mutex) int result = 0; pthread_mutex_t mx; - - if (mutex == NULL || *mutex == NULL) - { - return EINVAL; - } + /* + * Let the system deal with invalid pointers. + */ /* * We do a quick check to see if we need to do more work @@ -68,41 +66,51 @@ pthread_mutex_lock (pthread_mutex_t * mutex) mx = *mutex; - if (0 == InterlockedIncrement (&mx->lock_idx)) + if (mx->kind == PTHREAD_MUTEX_NORMAL) { - mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) PTW32_MUTEX_OWNER_ANONYMOUS); + if (0 != InterlockedIncrement (&mx->lock_idx)) + { + if (ptw32_semwait (&mx->wait_sema) != 0) + { + result = errno; + } + } } else { - if (mx->kind != PTHREAD_MUTEX_FAST_NP && - pthread_equal (mx->ownerThread, pthread_self ())) + if (0 == InterlockedIncrement (&mx->lock_idx)) { - (void) InterlockedDecrement (&mx->lock_idx); - - if (mx->kind == PTHREAD_MUTEX_RECURSIVE_NP) - { - mx->recursive_count++; - } - else - { - result = EDEADLK; - } + mx->recursive_count = 1; + mx->ownerThread = pthread_self (); } else { - if (ptw32_semwait (&mx->wait_sema) == 0) + pthread_t self = pthread_self(); + + if (pthread_equal (mx->ownerThread, self)) { - mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) PTW32_MUTEX_OWNER_ANONYMOUS); + (void) InterlockedDecrement (&mx->lock_idx); + + if (mx->kind == PTHREAD_MUTEX_RECURSIVE) + { + mx->recursive_count++; + } + else + { + result = EDEADLK; + } } else { - result = errno; + if (ptw32_semwait (&mx->wait_sema) == 0) + { + mx->recursive_count = 1; + mx->ownerThread = self; + } + else + { + result = errno; + } } } } diff --git a/pthread_mutex_timedlock.c b/pthread_mutex_timedlock.c index a5d6b59..58f5613 100644 --- a/pthread_mutex_timedlock.c +++ b/pthread_mutex_timedlock.c @@ -214,10 +214,9 @@ pthread_mutex_timedlock (pthread_mutex_t * mutex, return -1; #endif - if (mutex == NULL || *mutex == NULL) - { - return EINVAL; - } + /* + * Let the system deal with invalid pointers. + */ /* * We do a quick check to see if we need to do more work @@ -235,142 +234,135 @@ pthread_mutex_timedlock (pthread_mutex_t * mutex, mx = *mutex; - if (0 == InterlockedIncrement (&mx->lock_idx)) + if (mx->kind == PTHREAD_MUTEX_NORMAL) { - mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) PTW32_MUTEX_OWNER_ANONYMOUS); + if (0 != InterlockedIncrement (&mx->lock_idx)) + { + switch (ptw32_timed_semwait (&mx->wait_sema, abstime)) + { + case 0: /* We got the mutex. */ + { + break; + } + case 1: /* Timed out. */ + case 2: /* abstime passed before we started to wait. */ + { + /* + * If we timeout, it is up to us to adjust lock_idx to say + * we're no longer waiting. + * + * The owner thread may still have posted wait_sema thinking + * we were waiting. We must check but then NOT do any + * programmed work if we have acquired the mutex because + * we don't know how long ago abstime was. We MUST just release it + * immediately. + */ + EnterCriticalSection (&mx->wait_cs); + + result = ETIMEDOUT; + + if (-1 == sem_trywait (&mx->wait_sema)) + { + (void) InterlockedDecrement (&mx->lock_idx); + } + else + { + if (InterlockedDecrement (&mx->lock_idx) >= 0) + { + /* Someone else is waiting on that mutex */ + if (sem_post (&mx->wait_sema) != 0) + { + result = errno; + } + } + } + + LeaveCriticalSection (&mx->wait_cs); + break; + } + default: + { + result = errno; + break; + } + } + } } else { - if (mx->kind != PTHREAD_MUTEX_FAST_NP && - pthread_equal (mx->ownerThread, pthread_self ())) - { - (void) InterlockedDecrement (&mx->lock_idx); + pthread_t self = pthread_self(); - if (mx->kind == PTHREAD_MUTEX_RECURSIVE_NP) - { - mx->recursive_count++; - } - else - { - result = EDEADLK; - } + if (0 == InterlockedIncrement (&mx->lock_idx)) + { + mx->recursive_count = 1; + mx->ownerThread = self; } else { - if (abstime == NULL) + if (pthread_equal (mx->ownerThread, self)) { - result = EINVAL; + (void) InterlockedDecrement (&mx->lock_idx); + + if (mx->kind == PTHREAD_MUTEX_RECURSIVE) + { + mx->recursive_count++; + } + else + { + result = EDEADLK; + } } else { switch (ptw32_timed_semwait (&mx->wait_sema, abstime)) { - case 0: /* We got the mutex. */ - { - mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) - PTW32_MUTEX_OWNER_ANONYMOUS); - break; - } - case 1: /* Timedout, try a second grab. */ - { - int busy; - - EnterCriticalSection (&mx->wait_cs); - - /* - * If we timeout, it is up to us to adjust lock_idx to say - * we're no longer waiting. If the mutex was also unlocked - * while we were timing out, and we simply return ETIMEDOUT, - * then wait_sema would be left in a state that is not consistent - * with the state of lock_idx. - * - * We must check to see if wait_sema has just been posted - * but we can't just call sem_getvalue - we must compete for - * the semaphore using sem_trywait(), otherwise we would need - * additional critical sections elsewhere, which would make the - * logic too inefficient. - * - * If sem_trywait returns EAGAIN then either wait_sema - * was given directly to another waiting thread or - * another thread has called sem_*wait() before us and - * taken the lock. Then we MUST decrement lock_idx and return - * ETIMEDOUT. - * - * Otherwise we MUST return success (because we have effectively - * acquired the lock that would have been ours had we not - * timed out), and NOT decrement lock_idx. - * - * We can almost guarrantee that EAGAIN is the only - * possible error, so no need to test errno. - */ - - if (-1 == (busy = sem_trywait (&mx->wait_sema))) - { - (void) InterlockedDecrement (&mx->lock_idx); - result = ETIMEDOUT; - } - - LeaveCriticalSection (&mx->wait_cs); - - if (!busy) - { - /* - * We have acquired the lock on second grab - keep it. - */ - mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) - PTW32_MUTEX_OWNER_ANONYMOUS); - } - break; - } - case 2: /* abstime passed before we started to wait. */ - { - /* - * If we timeout, it is up to us to adjust lock_idx to say - * we're no longer waiting. - * - * The owner thread may still have posted wait_sema thinking - * we were waiting. I believe we must check but then NOT do any - * programmed work if we have acquired the mutex because - * we don't how long ago abstime was. We MUST just release it - * immediately. - */ - EnterCriticalSection (&mx->wait_cs); - - result = ETIMEDOUT; - - if (-1 == sem_trywait (&mx->wait_sema)) - { - (void) InterlockedDecrement (&mx->lock_idx); - } - else - { - if (InterlockedDecrement (&mx->lock_idx) >= 0) - { - /* Someone else is waiting on that mutex */ - if (sem_post (&mx->wait_sema) != 0) - { - result = errno; - } - } - } - - LeaveCriticalSection (&mx->wait_cs); - break; - } - default: - { - result = errno; - break; - } + case 0: /* We got the mutex. */ + { + mx->recursive_count = 1; + mx->ownerThread = self; + break; + } + case 1: /* Timedout. */ + case 2: /* abstime passed before we started to wait. */ + { + /* + * If we timeout, it is up to us to adjust lock_idx to say + * we're no longer waiting. + * + * The owner thread may still have posted wait_sema thinking + * we were waiting. We must check but then NOT do any + * programmed work if we have acquired the mutex because + * we don't know how long ago abstime was. We MUST just release it + * immediately. + */ + EnterCriticalSection (&mx->wait_cs); + + result = ETIMEDOUT; + + if (-1 == sem_trywait (&mx->wait_sema)) + { + (void) InterlockedDecrement (&mx->lock_idx); + } + else + { + if (InterlockedDecrement (&mx->lock_idx) >= 0) + { + /* Someone else is waiting on that mutex */ + if (sem_post (&mx->wait_sema) != 0) + { + result = errno; + } + } + } + + LeaveCriticalSection (&mx->wait_cs); + break; + } + default: + { + result = errno; + break; + } } } } diff --git a/pthread_mutex_trylock.c b/pthread_mutex_trylock.c index 6f137f0..bba8ed2 100644 --- a/pthread_mutex_trylock.c +++ b/pthread_mutex_trylock.c @@ -44,10 +44,9 @@ pthread_mutex_trylock (pthread_mutex_t * mutex) int result = 0; pthread_mutex_t mx; - if (mutex == NULL || *mutex == NULL) - { - return EINVAL; - } + /* + * Let the system deal with invalid pointers. + */ /* * We do a quick check to see if we need to do more work @@ -57,36 +56,36 @@ pthread_mutex_trylock (pthread_mutex_t * mutex) */ if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) { - result = ptw32_mutex_check_need_init (mutex); + if ((result = ptw32_mutex_check_need_init (mutex)) != 0) + { + return (result); + } } mx = *mutex; - if (result == 0) + if ((PTW32_INTERLOCKED_LONG) -1 == + ptw32_interlocked_compare_exchange ((PTW32_INTERLOCKED_LPLONG) & + mx->lock_idx, + (PTW32_INTERLOCKED_LONG) 0, + (PTW32_INTERLOCKED_LONG) -1)) { - if ((PTW32_INTERLOCKED_LONG) PTW32_MUTEX_LOCK_IDX_INIT == - ptw32_interlocked_compare_exchange ((PTW32_INTERLOCKED_LPLONG) & - mx->lock_idx, - (PTW32_INTERLOCKED_LONG) 0, - (PTW32_INTERLOCKED_LONG) - PTW32_MUTEX_LOCK_IDX_INIT)) + if (mx->kind != PTHREAD_MUTEX_NORMAL) { mx->recursive_count = 1; - mx->ownerThread = (mx->kind != PTHREAD_MUTEX_FAST_NP - ? pthread_self () - : (pthread_t) PTW32_MUTEX_OWNER_ANONYMOUS); + mx->ownerThread = pthread_self (); + } + } + else + { + if (mx->kind == PTHREAD_MUTEX_RECURSIVE && + pthread_equal (mx->ownerThread, pthread_self ())) + { + mx->recursive_count++; } else { - if (mx->kind == PTHREAD_MUTEX_RECURSIVE_NP && - pthread_equal (mx->ownerThread, pthread_self ())) - { - mx->recursive_count++; - } - else - { - result = EBUSY; - } + result = EBUSY; } } diff --git a/pthread_mutex_unlock.c b/pthread_mutex_unlock.c index 05677c8..7b20d79 100644 --- a/pthread_mutex_unlock.c +++ b/pthread_mutex_unlock.c @@ -44,10 +44,9 @@ pthread_mutex_unlock (pthread_mutex_t * mutex) int result = 0; pthread_mutex_t mx; - if (mutex == NULL || *mutex == NULL) - { - return EINVAL; - } + /* + * Let the system deal with invalid pointers. + */ mx = *mutex; @@ -58,30 +57,67 @@ pthread_mutex_unlock (pthread_mutex_t * mutex) */ if (mx < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) { - if (mx->ownerThread == (pthread_t) PTW32_MUTEX_OWNER_ANONYMOUS - || pthread_equal (mx->ownerThread, pthread_self ())) + if (mx->kind == PTHREAD_MUTEX_NORMAL) { - if (mx->kind != PTHREAD_MUTEX_RECURSIVE_NP - || 0 == --mx->recursive_count) - { - mx->ownerThread = NULL; - EnterCriticalSection (&mx->wait_cs); + LONG idx; + + idx = (LONG) ptw32_interlocked_compare_exchange ((PTW32_INTERLOCKED_LPLONG) + &mx->lock_idx, + (PTW32_INTERLOCKED_LONG) -1, + (PTW32_INTERLOCKED_LONG) 0); - if (InterlockedDecrement (&mx->lock_idx) >= 0) + if (idx != 0) + { + if (idx > 0) { - /* Someone is waiting on that mutex */ - if (sem_post (&mx->wait_sema) != 0) + EnterCriticalSection (&mx->wait_cs); + + if (InterlockedDecrement (&mx->lock_idx) >= 0) { - result = errno; + /* Someone is waiting on that mutex */ + if (sem_post (&mx->wait_sema) != 0) + { + result = errno; + } } - } - LeaveCriticalSection (&mx->wait_cs); + LeaveCriticalSection (&mx->wait_cs); + } + else + { + /* + * Was not locked (so can't be owned by us). + */ + result = EPERM; + } } } else { - result = EPERM; + if (pthread_equal (mx->ownerThread, pthread_self ())) + { + if (mx->kind != PTHREAD_MUTEX_RECURSIVE + || 0 == --mx->recursive_count) + { + mx->ownerThread = NULL; + EnterCriticalSection (&mx->wait_cs); + + if (InterlockedDecrement (&mx->lock_idx) >= 0) + { + /* Someone is waiting on that mutex */ + if (sem_post (&mx->wait_sema) != 0) + { + result = errno; + } + } + + LeaveCriticalSection (&mx->wait_cs); + } + } + else + { + result = EPERM; + } } } else diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 04f9943..cc36b53 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -273,7 +273,7 @@ sizes.pass: sizes.exe @ $(ECHO) Passed @ $(TOUCH) $@ -%.bench: $(XXLIBS) %.exe +%.bench: $(LIB) $(DLL) $(HDR) $(QAPC) $(XXLIBS) %.exe @ $(ECHO) Running $* $* @ $(ECHO) Done diff --git a/tests/benchlib.c b/tests/benchlib.c index 78ec3c8..3fa7171 100644 --- a/tests/benchlib.c +++ b/tests/benchlib.c @@ -51,6 +51,31 @@ int old_mutex_use = OLD_WIN32CS; BOOL (WINAPI *ptw32_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL; HINSTANCE ptw32_h_kernel32; +void +dummy_call(int * a) +{ +} + +void +interlocked_inc_with_conditionals(int * a) +{ + if (a != NULL) + if (InterlockedIncrement((long *) a) == -1) + { + *a = 0; + } +} + +void +interlocked_dec_with_conditionals(int * a) +{ + if (a != NULL) + if (InterlockedDecrement((long *) a) == -1) + { + *a = 0; + } +} + int old_mutex_init(old_mutex_t *mutex, const old_mutexattr_t *attr) { diff --git a/tests/benchtest.h b/tests/benchtest.h index 2401622..64af6c8 100644 --- a/tests/benchtest.h +++ b/tests/benchtest.h @@ -59,6 +59,9 @@ extern HINSTANCE ptw32_h_kernel32; #define PTW32_OBJECT_AUTO_INIT ((void *) -1) +void dummy_call(int * a); +void interlocked_inc_with_conditionals(int *a); +void interlocked_dec_with_conditionals(int *a); int old_mutex_init(old_mutex_t *mutex, const old_mutexattr_t *attr); int old_mutex_lock(old_mutex_t *mutex); int old_mutex_unlock(old_mutex_t *mutex); diff --git a/tests/benchtest1.c b/tests/benchtest1.c index 1a553dc..ce45cf2 100644 --- a/tests/benchtest1.c +++ b/tests/benchtest1.c @@ -101,6 +101,7 @@ runTest (char * testNameString, int mType) int main (int argc, char *argv[]) { + int i = 0; CRITICAL_SECTION cs; old_mutex_t ox; pthread_mutexattr_init(&ma); @@ -127,6 +128,45 @@ main (int argc, char *argv[]) overHeadMilliSecs = durationMilliSecs; + TESTSTART + assert((dummy_call(&i), 1) == one); + assert((dummy_call(&i), 1) == one); + TESTSTOP + + durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs; + + printf( "%-45s %15ld %15.3f\n", + "Dummy call x 2", + durationMilliSecs, + (float) durationMilliSecs * 1E3 / ITERATIONS); + + + TESTSTART + assert((interlocked_inc_with_conditionals(&i), 1) == one); + assert((interlocked_dec_with_conditionals(&i), 1) == one); + TESTSTOP + + durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs; + + printf( "%-45s %15ld %15.3f\n", + "Dummy call -> Interlocked with cond x 2", + durationMilliSecs, + (float) durationMilliSecs * 1E3 / ITERATIONS); + + + TESTSTART + assert((InterlockedIncrement(&i), 1) == one); + assert((InterlockedDecrement(&i), 1) == one); + TESTSTOP + + durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs; + + printf( "%-45s %15ld %15.3f\n", + "InterlockedOp x 2", + durationMilliSecs, + (float) durationMilliSecs * 1E3 / ITERATIONS); + + InitializeCriticalSection(&cs); TESTSTART @@ -139,7 +179,7 @@ main (int argc, char *argv[]) durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs; printf( "%-45s %15ld %15.3f\n", - "Simple Critical Section", + "Simple Critical Section x 2", durationMilliSecs, (float) durationMilliSecs * 1E3 / ITERATIONS); diff --git a/tests/mutex4.c b/tests/mutex4.c index 74ec159..75880fd 100644 --- a/tests/mutex4.c +++ b/tests/mutex4.c @@ -69,6 +69,9 @@ main() assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_DEFAULT) == 0); assert(pthread_mutex_init(&mutex1, &ma) == 0); assert(pthread_mutex_lock(&mutex1) == 0); + /* + * NORMAL (fast) mutexes don't check ownership. + */ assert(pthread_create(&t, NULL, unlocker, (void *) 0) == 0); assert(pthread_join(t, NULL) == 0); assert(pthread_mutex_unlock(&mutex1) == EPERM); @@ -78,6 +81,9 @@ main() assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_NORMAL) == 0); assert(pthread_mutex_init(&mutex1, &ma) == 0); assert(pthread_mutex_lock(&mutex1) == 0); + /* + * NORMAL (fast) mutexes don't check ownership. + */ assert(pthread_create(&t, NULL, unlocker, (void *) 0) == 0); assert(pthread_join(t, NULL) == 0); assert(pthread_mutex_unlock(&mutex1) == EPERM); -- cgit v1.2.3