diff options
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | FAQ | 14 | ||||
-rw-r--r-- | condvar.c | 148 | ||||
-rw-r--r-- | misc.c | 52 | ||||
-rw-r--r-- | mutex.c | 133 | ||||
-rw-r--r-- | pthread.h | 27 | ||||
-rw-r--r-- | tests/ChangeLog | 5 | ||||
-rw-r--r-- | tests/condvar3.c | 218 | ||||
-rw-r--r-- | tests/condvar4.c | 239 | ||||
-rw-r--r-- | tests/eyal1.c | 394 | ||||
-rw-r--r-- | tests/mutex2.c | 6 | ||||
-rw-r--r-- | tests/tsd1.c | 4 |
12 files changed, 657 insertions, 604 deletions
@@ -1,3 +1,24 @@ +Thu Mar 11 09:01:48 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * pthread.h (pthread_mutex_t): revert to (pthread_mutex_t *); + define a value to serve as PTHREAD_MUTEX_INITIALIZER. + (pthread_mutex_t_): remove staticinit and valid elements. + (pthread_cond_t): revert to (pthread_cond_t_ *); + define a value to serve as PTHREAD_COND_INITIALIZER. + (pthread_cond_t_): remove staticinit and valid elements. + + * mutex.c (pthread_mutex_t args): adjust indirection of references. + (all functions): check for PTHREAD_MUTEX_INITIALIZER value; + check for NULL (invalid). + + * condvar.c (pthread_cond_t args): adjust indirection of references. + (all functions): check for PTHREAD_COND_INITIALIZER value; + check for NULL (invalid). + +Wed Mar 10 17:18:12 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * misc.c (CancelableWait): Undo changes from Mar 8 and 7. + Mon Mar 8 11:18:59 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> * misc.c (CancelableWait): Ensure cancelEvent handle is the lowest @@ -5,22 +5,24 @@ INDEX ----- -Q 1 How do I get pthreads-win32 to link under Cygwin or Mingw32? +Q 1 Should I use Cygwin or Mingw32 as a development environment? Q 2 Now that pthreads-win32 builds under Mingw32, why do I get memory access violations? ============================================================================= -Q 1 How do I get pthreads-win32 to link under Cygwin or Mingw32? +Q 1 Should I use Cygwin or Mingw32 as a development environment? --- A 1 --- -The following email from Anders Norlander explains how to solve this -problem. I think the proviso is that the DLL and your application should -both be built with the same development environment (cygwin, mingw, -or MSVC etc). +Important: see Q2 also. + +I short, use Mingw32 with the MSVCRT library to build applications that use +the DLL. You cannot build the library itself with either yet because the +library uses C++ EH which is not thread-safe in egcs yet. Use MSVC or grab +the pre-build DLL etc. Date: Mon, 07 Dec 1998 15:11:37 +0100 From: Anders Norlander <anorland@hem2.passagen.se> @@ -19,7 +19,7 @@ _cond_check_need_init(pthread_cond_t *cond) /* * The following guarded test is specifically for statically - * initialised condition variables (via PTHREAD_COND_INITIALIZER). + * initialised condition variables (via PTHREAD_OBJECT_INITIALIZER). * * Note that by not providing this synchronisation we risk * introducing race conditions into applications which are @@ -31,38 +31,23 @@ _cond_check_need_init(pthread_cond_t *cond) * 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-cv state independently: - * - cv staticinit (true iff cv is static and still - * needs to be initialised) - * - cv valid (false iff cv has been destroyed) - * - * For example, in this implementation a cv initialised by - * PTHREAD_COND_INITIALIZER will be 'valid' but uninitialised until - * the thread attempts to use it. It can also be destroyed (made invalid) - * before ever being used. */ EnterCriticalSection(&_pthread_cond_test_init_lock); /* - * We got here because staticinit tested true, possibly under race - * conditions. Check staticinit again inside the critical section - * and only initialise if the cv is valid (not been destroyed). + * We got here possibly under race + * conditions. Check again inside the critical section. * If a static cv has been destroyed, the application can * re-initialise it only by calling pthread_cond_init() * explicitly. */ - if (cond->staticinit && cond->valid) + if (*cond == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) { result = pthread_cond_init(cond, NULL); } @@ -342,20 +327,13 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) */ { int result = EAGAIN; + pthread_cond_t cv; if (cond == NULL) { return EINVAL; } - /* - * Assuming any race condition here is harmless. - */ - if (cond->valid && !cond->staticinit) - { - return EBUSY; - } - if ((attr != NULL && *attr != NULL) && ((*attr)->pshared == PTHREAD_PROCESS_SHARED)) { @@ -368,34 +346,37 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) goto FAIL0; } - cond->waiters = 0; - cond->wasBroadcast = FALSE; + cv = (pthread_cond_t) calloc (1, sizeof (*cv)); - if (_pthread_sem_init (&(cond->sema), 0, 0) != 0) + if (cv == NULL) { + result = ENOMEM; goto FAIL0; } - if (pthread_mutex_init (&(cond->waitersLock), NULL) != 0) + + cv->waiters = 0; + cv->wasBroadcast = FALSE; + + if (_pthread_sem_init (&(cv->sema), 0, 0) != 0) + { + goto FAIL0; + } + if (pthread_mutex_init (&(cv->waitersLock), NULL) != 0) { goto FAIL1; } - cond->waitersDone = CreateEvent ( - 0, - (int) FALSE, /* manualReset */ - (int) FALSE, /* setSignaled */ - NULL); + cv->waitersDone = CreateEvent ( + 0, + (int) FALSE, /* manualReset */ + (int) FALSE, /* setSignaled */ + NULL); - if (cond->waitersDone == NULL) + if (cv->waitersDone == NULL) { goto FAIL2; } - cond->staticinit = 0; - - /* Mark as valid. */ - cond->valid = 1; - result = 0; goto DONE; @@ -406,13 +387,15 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) * ------------- */ FAIL2: - (void) pthread_mutex_destroy (&(cond->waitersLock)); + (void) pthread_mutex_destroy (&(cv->waitersLock)); FAIL1: - (void) _pthread_sem_destroy (&(cond->sema)); + (void) _pthread_sem_destroy (&(cv->sema)); FAIL0: DONE: + *cond = cv; + return (result); } /* pthread_cond_init */ @@ -447,25 +430,30 @@ pthread_cond_destroy (pthread_cond_t * cond) */ { int result = 0; + pthread_cond_t cv; /* * Assuming any race condition here is harmless. */ - if (cond == NULL || cond->valid == 0 || cond->staticinit == 1) + if (cond == NULL + || *cond == NULL) { return EINVAL; } - if (cond->waiters > 0) + cv = *cond; + + if (cv->waiters > 0) { return EBUSY; } - (void) _pthread_sem_destroy (&(cond->sema)); - (void) pthread_mutex_destroy (&(cond->waitersLock)); - (void) CloseHandle (cond->waitersDone); - - cond->valid = 0; + (void) _pthread_sem_destroy (&(cv->sema)); + (void) pthread_mutex_destroy (&(cv->waitersLock)); + (void) CloseHandle (cv->waitersDone); + + free(cv); + *cond = NULL; return (result); } @@ -478,14 +466,22 @@ cond_timedwait (pthread_cond_t * cond, int result = 0; int internal_result = 0; int lastWaiter = FALSE; + pthread_cond_t cv; + + if (cond == NULL || *cond == NULL) + { + return EINVAL; + } + + cv = *cond; /* * We do a quick check to see if we need to do more work - * to initialise a static condition variable. We check 'staticinit' + * to initialise a static condition variable. We check * again inside the guarded section of _cond_check_need_init() * to avoid race conditions. */ - if (cond->staticinit == 1) + if (cv == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) { result = _cond_check_need_init(cond); } @@ -493,7 +489,7 @@ cond_timedwait (pthread_cond_t * cond, /* * OK to increment cond->waiters because the caller locked 'mutex' */ - cond->waiters++; + cv->waiters++; /* * We keep the lock held just long enough to increment the count of @@ -518,23 +514,23 @@ cond_timedwait (pthread_cond_t * cond, */ pthread_cleanup_push (pthread_mutex_lock, mutex); - result = _pthread_sem_timedwait (&(cond->sema), abstime); + result = _pthread_sem_timedwait (&(cv->sema), abstime); pthread_cleanup_pop (0); } - if ((internal_result = pthread_mutex_lock (&(cond->waitersLock))) == 0) + if ((internal_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. */ - cond->waiters--; + cv->waiters--; - lastWaiter = cond->wasBroadcast && (cond->waiters == 0); + lastWaiter = cv->wasBroadcast && (cv->waiters == 0); - internal_result = pthread_mutex_unlock (&(cond->waitersLock)); + internal_result = pthread_mutex_unlock (&(cv->waitersLock)); } if (result == 0 && internal_result == 0) @@ -545,7 +541,7 @@ cond_timedwait (pthread_cond_t * cond, * If we are the last waiter on this broadcast * let the thread doing the broadcast proceed */ - if (!SetEvent (cond->waitersDone)) + if (!SetEvent (cv->waitersDone)) { result = EINVAL; } @@ -727,16 +723,19 @@ pthread_cond_signal (pthread_cond_t * cond) */ { int result = 0; + pthread_cond_t cv; - if (cond == NULL || cond->valid == 0) + if (cond == NULL || *cond == NULL) { return EINVAL; } + cv = *cond; + /* * No-op if the CV is static and hasn't been initialised yet. */ - if (cond->staticinit == 1) + if (cv == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) { return 0; } @@ -744,9 +743,9 @@ pthread_cond_signal (pthread_cond_t * cond) /* * If there aren't any waiters, then this is a no-op. */ - if (cond->waiters > 0) + if (cv->waiters > 0) { - result = _pthread_sem_post (&(cond->sema)); + result = _pthread_sem_post (&(cv->sema)); } return (result); @@ -793,18 +792,21 @@ pthread_cond_broadcast (pthread_cond_t * cond) { int result = 0; int i; + pthread_cond_t cv; - if (cond == NULL || cond->valid == 0) + if (cond == NULL || *cond == NULL) { return EINVAL; } - cond->wasBroadcast = TRUE; + cv = *cond; + + cv->wasBroadcast = TRUE; /* * No-op if the CV is static and hasn't been initialised yet. */ - if (cond->staticinit == 1) + if (cv == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) { return 0; } @@ -812,24 +814,21 @@ pthread_cond_broadcast (pthread_cond_t * cond) /* * Wake up all waiters */ - for (i = cond->waiters; i > 0 && result == 0; i--) + for (i = cv->waiters; i > 0 && result == 0; i--) { - - result = _pthread_sem_post (&(cond->sema)); + result = _pthread_sem_post (&(cv->sema)); } - if (cond->waiters > 0 && result == 0) + if (cv->waiters > 0 && result == 0) { /* * Wait for all the awakened threads to acquire their part of * the counting semaphore */ - if (WaitForSingleObject (cond->waitersDone, INFINITE) != + if (WaitForSingleObject (cv->waitersDone, INFINITE) != WAIT_OBJECT_0) { - result = 0; - } else { @@ -839,6 +838,7 @@ pthread_cond_broadcast (pthread_cond_t * cond) } return (result); + } /* </JEB> */ @@ -213,12 +213,8 @@ CancelableWait (HANDLE waitHandle, DWORD timeout) DWORD nHandles = 1; DWORD status; - /* - * If both objects are signaled, then (status - WAIT_OBJECT_0) - * will be the lowest index value of all the signaled objects. - * We must ensure that a cancelation is recognised if this occurs by - * placing the cancelEvent handle first in the handle table. - */ + handles[0] = waitHandle; + if ((self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey)) != NULL) { @@ -228,14 +224,16 @@ CancelableWait (HANDLE waitHandle, DWORD timeout) if (self->cancelState == PTHREAD_CANCEL_ENABLE) { - if ((handles[0] = self->cancelEvent) != NULL) + if ((handles[1] = self->cancelEvent) != NULL) { nHandles++; } } } - - handles[nHandles - 1] = waitHandle; + else + { + handles[1] = NULL; + } status = WaitForMultipleObjects ( nHandles, @@ -243,36 +241,41 @@ CancelableWait (HANDLE waitHandle, DWORD timeout) FALSE, timeout); + if (status == WAIT_FAILED) { result = EINVAL; + } else if (status == WAIT_TIMEOUT) { result = ETIMEDOUT; } - else if (status >= WAIT_ABANDONED_0 && status <= WAIT_ABANDONED_0 + nHandles - 1) + else if (status == WAIT_ABANDONED_0) { - /* - * The waitHandle was a mutex object that was abandoned. - */ result = EINVAL; } else { /* - * Either got the object or the cancel event - * was signaled, or both in which case the cancel - * event will be acted on. + * Either got the mutex or the cancel event + * was signaled */ - switch (status - WAIT_OBJECT_0 + 2 - nHandles) + switch (status - WAIT_OBJECT_0) { case 0: /* - * Got cancel request. + * Got the mutex + */ + result = 0; + break; + + case 1: + /* + * Got cancel request */ - ResetEvent (handles[0]); + ResetEvent (handles[1]); if (self != NULL && !self->implicit) { @@ -304,15 +307,8 @@ CancelableWait (HANDLE waitHandle, DWORD timeout) #endif /* _MSC_VER */ } - /* Should never get to here. */ - result = EINVAL; - break; - - case 1: - /* - * Got the object. - */ - result = 0; + /* Should never get to here. */ + result = EINVAL; break; default: @@ -30,38 +30,24 @@ _mutex_check_need_init(pthread_mutex_t *mutex) * 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 mutex is static and still - * needs to be initialised) - * - mutex valid (false iff mutex has been destroyed) - * - * For example, in this implementation a mutex initialised by - * PTHREAD_MUTEX_INITIALIZER 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, possibly under race - * conditions. Check staticinit again inside the critical section + * We got here possibly under race + * conditions. Check 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) + if (*mutex == (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) { result = pthread_mutex_init(mutex, NULL); } @@ -75,22 +61,25 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { int result = 0; + pthread_mutex_t mx; if (mutex == NULL) { return EINVAL; } - mutex->mutex = 0; + mx = *mutex; - /* - * Assuming any race condition here is harmless. - */ - if (mutex->valid && !mutex->staticinit) + mx = (pthread_mutex_t) calloc(1, sizeof(*mx)); + + if (mx == NULL) { - return EBUSY; + result = ENOMEM; + goto FAIL0; } + mx->mutex = 0; + if (attr != NULL && *attr != NULL && (*attr)->pshared == PTHREAD_PROCESS_SHARED @@ -108,11 +97,11 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) #error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet. - mutex->mutex = CreateMutex ( + mx->mutex = CreateMutex ( NULL, FALSE, ????); - result = (mutex->mutex == 0) ? EAGAIN : 0; + result = (mx->mutex == 0) ? EAGAIN : 0; #else @@ -131,7 +120,7 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) /* * Create a critical section. */ - InitializeCriticalSection(&mutex->cs); + InitializeCriticalSection(&mx->cs); } else { @@ -139,20 +128,21 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) * Create a mutex that can only be used within the * current process */ - mutex->mutex = CreateMutex (NULL, - FALSE, - NULL); - result = (mutex->mutex == 0) ? EAGAIN : 0; + mx->mutex = CreateMutex (NULL, + FALSE, + NULL); + + if (mx->mutex == 0) + { + result = EAGAIN; + mx = NULL; + goto FAIL0; + } } } - if (result == 0) - { - mutex->staticinit = 0; - - /* Mark as valid. */ - mutex->valid = 1; - } +FAIL0: + *mutex = mx; return(result); } @@ -161,33 +151,35 @@ int pthread_mutex_destroy(pthread_mutex_t *mutex) { int result = 0; + pthread_mutex_t mx; - if (mutex == NULL) + if (mutex == NULL + || *mutex == NULL) { return EINVAL; } + mx = *mutex; + /* * Check to see if we have something to delete. */ - if (!mutex->staticinit) + if (mx != (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) { - if (mutex->mutex == 0) + if (mx->mutex == 0) { - DeleteCriticalSection(&mutex->cs); + DeleteCriticalSection(&mx->cs); } else { - result = (CloseHandle (mutex->mutex) ? 0 : EINVAL); + result = (CloseHandle (mx->mutex) ? 0 : EINVAL); } } if (result == 0) { - mutex->mutex = 0; - - /* Mark as invalid. */ - mutex->valid = 0; + mx->mutex = 0; + *mutex = NULL; } return(result); @@ -444,27 +436,35 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) { int result = 0; + pthread_mutex_t mx; + + if (mutex == NULL || *mutex == NULL) + { + return EINVAL; + } /* * We do a quick check to see if we need to do more work - * to initialise a static mutex. We check 'staticinit' + * to initialise a static mutex. We check * again inside the guarded section of _mutex_check_need_init() * to avoid race conditions. */ - if (mutex->staticinit == 1) + if (*mutex == (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) { result = _mutex_check_need_init(mutex); } + mx = *mutex; + if (result == 0) { - if (mutex->mutex == 0) + if (mx->mutex == 0) { - EnterCriticalSection(&mutex->cs); + EnterCriticalSection(&mx->cs); } else { - result = (WaitForSingleObject(mutex->mutex, INFINITE) + result = (WaitForSingleObject(mx->mutex, INFINITE) == WAIT_OBJECT_0) ? 0 : EINVAL; @@ -478,21 +478,29 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) { int result = 0; + pthread_mutex_t mx; + + if (mutex == NULL || *mutex == NULL) + { + return EINVAL; + } + + mx = *mutex; /* * 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) + if (mx != (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) { - if (mutex->mutex == 0) + if (mx->mutex == 0) { - LeaveCriticalSection(&mutex->cs); + LeaveCriticalSection(&mx->cs); } else { - result = (ReleaseMutex (mutex->mutex) ? 0 : EINVAL); + result = (ReleaseMutex (mx->mutex) ? 0 : EINVAL); } } else @@ -507,28 +515,31 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex) { int result = 0; + pthread_mutex_t mx; - if (mutex == NULL) + if (mutex == NULL || *mutex == NULL) { return EINVAL; } /* * We do a quick check to see if we need to do more work - * to initialise a static mutex. We check 'staticinit' + * to initialise a static mutex. We check * again inside the guarded section of _mutex_check_need_init() * to avoid race conditions. */ - if (mutex->staticinit) + if (*mutex == (pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) { result = _mutex_check_need_init(mutex); } + mx = *mutex; + if (result == 0) { - if (mutex->mutex == 0) + if (mx->mutex == 0) { - if ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) + if ((*_pthread_try_enter_critical_section)(&mx->cs) != TRUE) { result = EBUSY; } @@ -537,7 +548,7 @@ pthread_mutex_trylock(pthread_mutex_t *mutex) { DWORD status; - status = WaitForSingleObject (mutex->mutex, 0); + status = WaitForSingleObject (mx->mutex, 0); if (status != WAIT_OBJECT_0) { @@ -419,9 +419,9 @@ extern "C" 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_mutex_t_ *pthread_mutex_t; typedef struct pthread_mutexattr_t_ *pthread_mutexattr_t; - typedef struct pthread_cond_t_ pthread_cond_t; + typedef struct pthread_cond_t_ *pthread_cond_t; typedef struct pthread_condattr_t_ *pthread_condattr_t; @@ -567,16 +567,19 @@ struct pthread_attr_t_ { /* * ==================== * ==================== - * Mutex + * Mutexes and Condition Variables * ==================== * ==================== */ -#define PTHREAD_MUTEX_INITIALIZER { 1, 1 /* Remaining are all 0 */ } +enum { + _PTHREAD_OBJECT_INVALID = 0, /* NULL */ + _PTHREAD_OBJECT_AUTO_INIT +}; + +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) _PTHREAD_OBJECT_AUTO_INIT) struct pthread_mutex_t_ { - int staticinit; /* Needs implicit init if 1. */ - int valid; /* Not destroyed if 1. */ HANDLE mutex; CRITICAL_SECTION cs; }; @@ -596,21 +599,11 @@ struct pthread_key_t_ { }; -/* - * ==================== - * ==================== - * Condition Variable - * ==================== - * ==================== - */ - -#define PTHREAD_COND_INITIALIZER { 1, 1 /* Remaining are all 0 */ } +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT) typedef HANDLE _pthread_sem_t; struct pthread_cond_t_ { - int staticinit; /* Needs implicit init if 1. */ - int valid; /* Not destroyed if 1. */ long waiters; /* # waiting threads */ pthread_mutex_t waitersLock; /* Mutex that guards access to waiter count */ diff --git a/tests/ChangeLog b/tests/ChangeLog index aef418f..0304802 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,8 @@ +Fri Mar 12 08:34:15 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> + + * eyal1.c (main): Fix trylock loop; was not waiting for thread to lock + the "started" mutex. + Sun Mar 7 10:41:52 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au> * Makefile (condvar3, condvar4): Add tests. diff --git a/tests/condvar3.c b/tests/condvar3.c index 8dc56af..27223ab 100644 --- a/tests/condvar3.c +++ b/tests/condvar3.c @@ -1,109 +1,109 @@ -/*
- * File: condvar3.c
- *
- * Test Synopsis:
- * - Test basic function of a CV
- *
- * Test Method (Validation or Falsification):
- * - Validation
- *
- * Requirements Tested:
- * -
- *
- * Features Tested:
- * -
- *
- * Cases Tested:
- * -
- *
- * Description:
- * - The primary thread takes the lock before creating any threads.
- * The secondary thread blocks on the lock allowing the primary
- * thread to enter the cv wait state which releases the lock.
- * The secondary thread then takes the lock and signals the waiting
- * primary thread.
- *
- * Environment:
- * -
- *
- * Input:
- * - None.
- *
- * Output:
- * - File name, Line number, and failed expression on failure.
- * - No output on success.
- *
- * Assumptions:
- * -
- *
- * Pass Criteria:
- * - pthread_cond_timedwait returns 0.
- * - Process returns zero exit status.
- *
- * Fail Criteria:
- * - pthread_cond_timedwait returns ETIMEDOUT.
- * - Process returns non-zero exit status.
- */
-
-#include "test.h"
-#include <sys/timeb.h>
-
-pthread_cond_t cv;
-pthread_mutex_t mutex;
-
-enum {
- NUMTHREADS = 2 /* Including the primary thread. */
-};
-
-void *
-mythread(void * arg)
-{
- Sleep(1);
-
- assert(pthread_mutex_lock(&mutex) == 0);
-
- assert(pthread_cond_signal(&cv) == 0);
-
- assert(pthread_mutex_unlock(&mutex) == 0);
-
- return 0;
-}
-
-int
-main()
-{
- pthread_t t[NUMTHREADS];
- struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
- struct _timeb currSysTime;
-#endif
- const DWORD NANOSEC_PER_MILLISEC = 1000000;
-
- assert((t[0] = pthread_self()) != NULL);
-
- assert(pthread_cond_init(&cv, NULL) == 0);
-
- assert(pthread_mutex_init(&mutex, NULL) == 0);
-
- assert(pthread_mutex_lock(&mutex) == 0);
-
- /* get current system time */
- _ftime(&currSysTime);
-
- abstime.tv_sec = currSysTime.time;
- abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
-
- assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);
-
- abstime.tv_sec += 5;
-
- assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == 0);
-
- assert(pthread_mutex_unlock(&mutex) == 0);
-
- assert(pthread_cond_destroy(&cv) == 0);
-
- return 0;
-}
+/* + * File: condvar3.c + * + * Test Synopsis: + * - Test basic function of a CV + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - The primary thread takes the lock before creating any threads. + * The secondary thread blocks on the lock allowing the primary + * thread to enter the cv wait state which releases the lock. + * The secondary thread then takes the lock and signals the waiting + * primary thread. + * + * Environment: + * - + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * - + * + * Pass Criteria: + * - pthread_cond_timedwait returns 0. + * - Process returns zero exit status. + * + * Fail Criteria: + * - pthread_cond_timedwait returns ETIMEDOUT. + * - Process returns non-zero exit status. + */ + +#include "test.h" +#include <sys/timeb.h> + +pthread_cond_t cv; +pthread_mutex_t mutex; + +enum { + NUMTHREADS = 2 /* Including the primary thread. */ +}; + +void * +mythread(void * arg) +{ + Sleep(1); + + assert(pthread_mutex_lock(&mutex) == 0); + + assert(pthread_cond_signal(&cv) == 0); + + assert(pthread_mutex_unlock(&mutex) == 0); + + return 0; +} + +int +main() +{ + pthread_t t[NUMTHREADS]; + struct timespec abstime = { 0, 0 }; +#if defined(__MINGW32__) + struct timeb currSysTime; +#else + struct _timeb currSysTime; +#endif + const DWORD NANOSEC_PER_MILLISEC = 1000000; + + assert((t[0] = pthread_self()) != NULL); + + assert(pthread_cond_init(&cv, NULL) == 0); + + assert(pthread_mutex_init(&mutex, NULL) == 0); + + assert(pthread_mutex_lock(&mutex) == 0); + + /* get current system time */ + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0); + + abstime.tv_sec += 5; + + assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == 0); + + assert(pthread_mutex_unlock(&mutex) == 0); + + assert(pthread_cond_destroy(&cv) == 0); + + return 0; +} diff --git a/tests/condvar4.c b/tests/condvar4.c index 4c5fc38..f93db27 100644 --- a/tests/condvar4.c +++ b/tests/condvar4.c @@ -1,121 +1,118 @@ -/*
- * File: condvar1.c
- *
- * Test Synopsis:
- * - Test PTHREAD_COND_INITIALIZER.
- *
- * Test Method (Validation or Falsification):
- * - Validation
- *
- * Requirements Tested:
- * -
- *
- * Features Tested:
- * -
- *
- * Cases Tested:
- * -
- *
- * Description:
- * - Test basic CV function but starting with a static initialised
- * CV.
- *
- * Environment:
- * -
- *
- * Input:
- * - None.
- *
- * Output:
- * - File name, Line number, and failed expression on failure.
- * - No output on success.
- *
- * Assumptions:
- * -
- *
- * Pass Criteria:
- * - pthread_cond_timedwait returns 0.
- * - Process returns zero exit status.
- *
- * Fail Criteria:
- * - pthread_cond_timedwait returns ETIMEDOUT.
- * - Process returns non-zero exit status.
- */
-
-#include "test.h"
-#include <sys/timeb.h>
-
-typedef struct cvthing_t_ cvthing_t;
-
-struct cvthing_t_ {
- pthread_cond_t notbusy;
- pthread_mutex_t lock;
-};
-
-static cvthing_t cvthing = {
- PTHREAD_MUTEX_INITIALIZER,
- PTHREAD_COND_INITIALIZER,
-};
-
-enum {
- NUMTHREADS = 2
-};
-
-void *
-mythread(void * arg)
-{
- assert(pthread_mutex_lock(&cvthing.lock) == 0);
-
- assert(pthread_cond_signal(&cvthing.notbusy) == 0);
-
- assert(pthread_mutex_unlock(&cvthing.lock) == 0);
-
- return 0;
-}
-
-int
-main()
-{
- pthread_t t[NUMTHREADS];
- struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
- struct _timeb currSysTime;
-#endif
- const DWORD NANOSEC_PER_MILLISEC = 1000000;
-
- assert(cvthing.notbusy.staticinit == 1);
- assert(cvthing.notbusy.valid == 1);
-
- assert((t[0] = pthread_self()) != NULL);
-
- assert(pthread_mutex_lock(&cvthing.lock) == 0);
-
- /* get current system time */
- _ftime(&currSysTime);
-
- abstime.tv_sec = currSysTime.time;
- abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
-
- abstime.tv_sec += 5;
-
- assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == ETIMEDOUT);
-
- assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);
-
- _ftime(&currSysTime);
-
- abstime.tv_sec = currSysTime.time;
- abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
-
- abstime.tv_sec += 5;
-
- assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
-
- assert(pthread_mutex_unlock(&cvthing.lock) == 0);
-
- assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
-
- return 0;
-}
+/* + * File: condvar1.c + * + * Test Synopsis: + * - Test PTHREAD_COND_INITIALIZER. + * + * Test Method (Validation or Falsification): + * - Validation + * + * Requirements Tested: + * - + * + * Features Tested: + * - + * + * Cases Tested: + * - + * + * Description: + * - Test basic CV function but starting with a static initialised + * CV. + * + * Environment: + * - + * + * Input: + * - None. + * + * Output: + * - File name, Line number, and failed expression on failure. + * - No output on success. + * + * Assumptions: + * - + * + * Pass Criteria: + * - pthread_cond_timedwait returns 0. + * - Process returns zero exit status. + * + * Fail Criteria: + * - pthread_cond_timedwait returns ETIMEDOUT. + * - Process returns non-zero exit status. + */ + +#include "test.h" +#include <sys/timeb.h> + +typedef struct cvthing_t_ cvthing_t; + +struct cvthing_t_ { + pthread_cond_t notbusy; + pthread_mutex_t lock; +}; + +static cvthing_t cvthing = { + PTHREAD_MUTEX_INITIALIZER, + PTHREAD_COND_INITIALIZER, +}; + +enum { + NUMTHREADS = 2 +}; + +void * +mythread(void * arg) +{ + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + assert(pthread_cond_signal(&cvthing.notbusy) == 0); + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + return 0; +} + +int +main() +{ + pthread_t t[NUMTHREADS]; + struct timespec abstime = { 0, 0 }; +#if defined(__MINGW32__) + struct timeb currSysTime; +#else + struct _timeb currSysTime; +#endif + const DWORD NANOSEC_PER_MILLISEC = 1000000; + + assert((t[0] = pthread_self()) != NULL); + + assert(pthread_mutex_lock(&cvthing.lock) == 0); + + /* get current system time */ + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + abstime.tv_sec += 5; + + assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == ETIMEDOUT); + + assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0); + + _ftime(&currSysTime); + + abstime.tv_sec = currSysTime.time; + abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm; + + abstime.tv_sec += 5; + + assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0); + + assert(pthread_mutex_unlock(&cvthing.lock) == 0); + + assert(pthread_cond_destroy(&cvthing.notbusy) == 0); + + return 0; +} diff --git a/tests/eyal1.c b/tests/eyal1.c index 4345452..533822d 100644 --- a/tests/eyal1.c +++ b/tests/eyal1.c @@ -49,15 +49,16 @@ #include <math.h> struct thread_control { - int id; - pthread_t thread; /* thread id */ - pthread_mutex_t mutex_start; - pthread_mutex_t mutex_started; - pthread_mutex_t mutex_end; - pthread_mutex_t mutex_ended; - long work; /* work done */ - int stat; /* pthread_init status */ + int id; + pthread_t thread; /* thread id */ + pthread_mutex_t mutex_start; + pthread_mutex_t mutex_started; + pthread_mutex_t mutex_end; + pthread_mutex_t mutex_ended; + long work; /* work done */ + int stat; /* pthread_init status */ }; + typedef struct thread_control TC; static TC *tcs = NULL; @@ -70,229 +71,250 @@ static int todo = -1; static pthread_mutex_t mutex_todo = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t mutex_stdout = PTHREAD_MUTEX_INITIALIZER; -/*static pthread_attr_t pthread_attr_default;*/ - static void die (int ret) { - if (NULL != tcs) { - free (tcs); - tcs = NULL; - } - - if (ret) - exit (ret); + if (NULL != tcs) + { + free (tcs); + tcs = NULL; + } + + if (ret) + exit (ret); } static void waste_time (int n) { - int i; - double f; + int i; + double f; - f = rand (); + f = rand (); - for (i = n*100; i > 0; --i) { - f = sqrt (f) * f + 10000.0; - } + for (i = n*100; i > 0; --i) + { + f = sqrt (f) * f + 10000.0; + } } static int do_work_unit (int who, int n) { - int i; - static int nchars = 0; + int i; + static int nchars = 0; - if (quiet) - i = 0; - else { -/* get lock on stdout -*/ - assert(pthread_mutex_lock (&mutex_stdout) == 0); + if (quiet) + i = 0; + else { + /* + * get lock on stdout + */ + assert(pthread_mutex_lock (&mutex_stdout) == 0); -/* do our job -*/ - i = printf ("%c", - "0123456789abcdefghijklmnopqrstuvwxyz"[who]); - if (!(++nchars % 50)) - printf ("\n"); - fflush (stdout); + /* + * do our job + */ + i = printf ("%c", "0123456789abcdefghijklmnopqrstuvwxyz"[who]); -/* release lock on stdout -*/ - assert(pthread_mutex_unlock (&mutex_stdout) == 0); - } + if (!(++nchars % 50)) + printf ("\n"); - n = rand () % 10000; /* ignore incoming 'n' */ - waste_time (n); + fflush (stdout); - return (n); + /* + * release lock on stdout + */ + assert(pthread_mutex_unlock (&mutex_stdout) == 0); + } + + n = rand () % 10000; /* ignore incoming 'n' */ + waste_time (n); + + return (n); } static int print_server (void *ptr) { - int mywork; - int n; - TC *tc = (TC *)ptr; - - assert(pthread_mutex_lock (&tc->mutex_started) == 0); - - for (;;) { - assert(pthread_mutex_lock (&tc->mutex_start) == 0); - assert(pthread_mutex_unlock (&tc->mutex_start) == 0); - assert(pthread_mutex_lock (&tc->mutex_ended) == 0); - assert(pthread_mutex_unlock (&tc->mutex_started) == 0); + int mywork; + int n; + TC *tc = (TC *)ptr; + + assert(pthread_mutex_lock (&tc->mutex_started) == 0); + + for (;;) + { + assert(pthread_mutex_lock (&tc->mutex_start) == 0); + assert(pthread_mutex_unlock (&tc->mutex_start) == 0); + assert(pthread_mutex_lock (&tc->mutex_ended) == 0); + assert(pthread_mutex_unlock (&tc->mutex_started) == 0); + + for (;;) + { + + /* + * get lock on todo list + */ + assert(pthread_mutex_lock (&mutex_todo) == 0); + + mywork = todo; + if (todo >= 0) + { + ++todo; + if (todo >= nwork) + todo = -1; + } + assert(pthread_mutex_unlock (&mutex_todo) == 0); + + if (mywork < 0) + break; + + assert((n = do_work_unit (tc->id, mywork)) >= 0); + tc->work += n; + } - for (;;) { + assert(pthread_mutex_lock (&tc->mutex_end) == 0); + assert(pthread_mutex_unlock (&tc->mutex_end) == 0); + assert(pthread_mutex_lock (&tc->mutex_started) == 0); + assert(pthread_mutex_unlock (&tc->mutex_ended) == 0); -/* get lock on todo list -*/ - assert(pthread_mutex_lock (&mutex_todo) == 0); - - mywork = todo; - if (todo >= 0) { - ++todo; - if (todo >= nwork) - todo = -1; - } - assert(pthread_mutex_unlock (&mutex_todo) == 0); - - if (mywork < 0) - break; - - assert((n = do_work_unit (tc->id, mywork)) >= 0); - tc->work += n; - } - - assert(pthread_mutex_lock (&tc->mutex_end) == 0); - assert(pthread_mutex_unlock (&tc->mutex_end) == 0); - assert(pthread_mutex_lock (&tc->mutex_started) == 0); - assert(pthread_mutex_unlock (&tc->mutex_ended) == 0); - - if (-2 == mywork) - break; - } + if (-2 == mywork) + break; + } - assert(pthread_mutex_unlock (&tc->mutex_started) == 0); + assert(pthread_mutex_unlock (&tc->mutex_started) == 0); - return (0); + return (0); } static void dosync (void) { - int i; - - for (i = 0; i < nthreads; ++i) { - assert(pthread_mutex_lock (&tcs[i].mutex_end) == 0); - assert(pthread_mutex_unlock (&tcs[i].mutex_start) == 0); - assert(pthread_mutex_lock (&tcs[i].mutex_started) == 0); - assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); - } - -/* Now threads do their work -*/ - for (i = 0; i < nthreads; ++i) { - assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); - assert(pthread_mutex_unlock (&tcs[i].mutex_end) == 0); - assert(pthread_mutex_lock (&tcs[i].mutex_ended) == 0); - assert(pthread_mutex_unlock (&tcs[i].mutex_ended) == 0); - } + int i; + + for (i = 0; i < nthreads; ++i) + { + assert(pthread_mutex_lock (&tcs[i].mutex_end) == 0); + assert(pthread_mutex_unlock (&tcs[i].mutex_start) == 0); + assert(pthread_mutex_lock (&tcs[i].mutex_started) == 0); + assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); + } + + /* + * Now threads do their work + */ + for (i = 0; i < nthreads; ++i) + { + assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); + assert(pthread_mutex_unlock (&tcs[i].mutex_end) == 0); + assert(pthread_mutex_lock (&tcs[i].mutex_ended) == 0); + assert(pthread_mutex_unlock (&tcs[i].mutex_ended) == 0); + } } static void dowork (void) { - todo = 0; - dosync(); + todo = 0; + dosync(); - todo = 0; - dosync(); + todo = 0; + dosync(); } int main (int argc, char *argv[]) { - int i; - - assert(NULL != (tcs = calloc (nthreads, sizeof (*tcs)))); - -/* Launch threads -*/ - for (i = 0; i < nthreads; ++i) { - tcs[i].id = i; - - assert(pthread_mutex_init (&tcs[i].mutex_start, NULL) == 0); - assert(pthread_mutex_init (&tcs[i].mutex_started, NULL) == 0); - assert(pthread_mutex_init (&tcs[i].mutex_end, NULL) == 0); - assert(pthread_mutex_init (&tcs[i].mutex_ended, NULL) == 0); - - tcs[i].work = 0; - - assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); - assert((tcs[i].stat = - pthread_create (&tcs[i].thread, - NULL, - (void*)&print_server, (void *)&tcs[i]) - ) == 0); - -/* Wait for thread initialisation -*/ - while (1) - { - int trylock; - - trylock = pthread_mutex_trylock(&tcs[i].mutex_started); - assert(trylock == 0 || trylock == EBUSY); - - if (trylock == 0) - { - assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); - break; - } - } - } - - dowork (); - -/* Terminate threads -*/ - todo = -2; /* please terminate */ - dosync(); - - for (i = 0; i < nthreads; ++i) { - if (0 == tcs[i].stat) - assert(pthread_join (tcs[i].thread, NULL) == 0); - } - -/* destroy locks -*/ - assert(pthread_mutex_destroy (&mutex_stdout) == 0); - assert(pthread_mutex_destroy (&mutex_todo) == 0); - -/* Cleanup -*/ - printf ("\n"); - -/* Show results -*/ - for (i = 0; i < nthreads; ++i) { - printf ("%2d ", i); - if (0 == tcs[i].stat) - printf ("%10ld\n", tcs[i].work); - else - printf ("failed %d\n", tcs[i].stat); - - assert(pthread_mutex_destroy (&tcs[i].mutex_start) == 0); - assert(pthread_mutex_destroy (&tcs[i].mutex_started) == 0); - assert(pthread_mutex_destroy (&tcs[i].mutex_end) == 0); - assert(pthread_mutex_destroy (&tcs[i].mutex_ended) == 0); - } - - die (0); - - return (0); + int i; + + assert(NULL != (tcs = calloc (nthreads, sizeof (*tcs)))); + + /* + * Launch threads + */ + for (i = 0; i < nthreads; ++i) + { + tcs[i].id = i; + + assert(pthread_mutex_init (&tcs[i].mutex_start, NULL) == 0); + assert(pthread_mutex_init (&tcs[i].mutex_started, NULL) == 0); + assert(pthread_mutex_init (&tcs[i].mutex_end, NULL) == 0); + assert(pthread_mutex_init (&tcs[i].mutex_ended, NULL) == 0); + + tcs[i].work = 0; + + assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0); + assert((tcs[i].stat = + pthread_create (&tcs[i].thread, + NULL, + (void*)&print_server, (void *)&tcs[i]) + ) == 0); + + /* + * Wait for thread initialisation + */ + { + int trylock = 0; + + while (trylock == 0) + { + trylock = pthread_mutex_trylock(&tcs[i].mutex_started); + assert(trylock == 0 || trylock == EBUSY); + + if (trylock == 0) + { + assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0); + } + } + } + } + + dowork (); + + /* + * Terminate threads + */ + todo = -2; /* please terminate */ + dosync(); + + for (i = 0; i < nthreads; ++i) + { + if (0 == tcs[i].stat) + assert(pthread_join (tcs[i].thread, NULL) == 0); + } + + /* + * destroy locks + */ + assert(pthread_mutex_destroy (&mutex_stdout) == 0); + assert(pthread_mutex_destroy (&mutex_todo) == 0); + + /* + * Cleanup + */ + printf ("\n"); + + /* + * Show results + */ + for (i = 0; i < nthreads; ++i) + { + printf ("%2d ", i); + if (0 == tcs[i].stat) + printf ("%10ld\n", tcs[i].work); + else + printf ("failed %d\n", tcs[i].stat); + + assert(pthread_mutex_destroy (&tcs[i].mutex_start) == 0); + assert(pthread_mutex_destroy (&tcs[i].mutex_started) == 0); + assert(pthread_mutex_destroy (&tcs[i].mutex_end) == 0); + assert(pthread_mutex_destroy (&tcs[i].mutex_ended) == 0); + } + + die (0); + + return (0); } diff --git a/tests/mutex2.c b/tests/mutex2.c index 7956f14..b161899 100644 --- a/tests/mutex2.c +++ b/tests/mutex2.c @@ -16,8 +16,14 @@ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int main() { + assert(mutex == PTHREAD_MUTEX_INITIALIZER); + assert(pthread_mutex_lock(&mutex) == 0); + assert(mutex != PTHREAD_MUTEX_INITIALIZER); + + assert(mutex != NULL); + assert(pthread_mutex_unlock(&mutex) == 0); return 0; diff --git a/tests/tsd1.c b/tests/tsd1.c index 93403b1..9448c10 100644 --- a/tests/tsd1.c +++ b/tests/tsd1.c @@ -146,7 +146,7 @@ main() assert(pthread_key_delete(key) == 0); - for (i = 0; i < 10; i++) + for (i = 1; i < 10; i++) { /* * The counter is incremented once when the key is set to @@ -165,5 +165,5 @@ main() fflush(stderr); - return (fail) ? 1 : 0; + return (fail); } |