summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog46
-rw-r--r--condvar.c210
-rw-r--r--global.c6
-rw-r--r--implement.h1
-rw-r--r--misc.c52
-rw-r--r--mutex.c2
-rw-r--r--private.c6
-rw-r--r--pthread.h53
-rw-r--r--semaphore.c10
-rw-r--r--tests/Makefile139
-rw-r--r--tests/condvar2.c156
-rw-r--r--tests/condvar3.c212
-rw-r--r--tests/condvar4.c227
13 files changed, 658 insertions, 462 deletions
diff --git a/ChangeLog b/ChangeLog
index cdce59a..5f50d64 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,49 @@
+Mon Mar 8 11:18:59 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * misc.c (CancelableWait): Ensure cancelEvent handle is the lowest
+ indexed element in the handles array. Enhance test for abandoned
+ objects.
+
+ * pthread.h (PTHREAD_MUTEX_INITIALIZER): Trailing elements not
+ initialised are set to zero by the compiler. This avoids the
+ problem of initialising the opaque critical section element in it.
+ (PTHREAD_COND_INITIALIZER): Ditto.
+
+ * semaphore.c (_pthread_sem_timedwait): Check sem == NULL earlier.
+
+Sun Mar 7 12:31:14 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * condvar.c (pthread_cond_init): set semaphore initial value
+ to 0, not 1. cond_timedwait was returning signaled immediately.
+
+ * misc.c (CancelableWait): Place the cancel event handle first
+ in the handle table for WaitForMultipleObjects. This ensures that
+ the cancel event is recognised and acted apon if both objects
+ happen to be signaled together.
+
+ * private.c (_pthread_cond_test_init_lock): Initialise and destroy.
+
+ * implement.h (_pthread_cond_test_init_lock): Add extern.
+
+ * global.c (_pthread_cond_test_init_lock): Add declaration.
+
+ * condvar.c (pthread_cond_destroy): check for valid initialised CV;
+ flag destroyed CVs as invalid.
+ (pthread_cond_init): pthread_cond_t is no longer just a pointer.
+ This is because PTHREAD_COND_INITIALIZER needs state info to reside
+ in pthread_cond_t so that it can initialise on first use. Will work on
+ making pthread_cond_t (and other objects like it) opaque again, if
+ possible, later.
+ (cond_timedwait): add check for statically initialisation of
+ CV; initialise on first use.
+ (pthread_cond_signal): check for valid CV.
+ (pthread_cond_broadcast): check for valid CV.
+ (_cond_check_need_init): Add.
+
+ * pthread.h (PTHREAD_COND_INITIALIZER): Fix.
+ (pthread_cond_t): no longer a pointer to pthread_cond_t_.
+ (pthread_cond_t_): add 'staticinit' and 'valid' elements.
+
Sun Feb 21 1999 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* pthread.h (PTHREAD_MUTEX_INITIALIZER): missing braces around
diff --git a/condvar.c b/condvar.c
index eb549b5..8fca94c 100644
--- a/condvar.c
+++ b/condvar.c
@@ -12,6 +12,67 @@
#include "pthread.h"
#include "implement.h"
+static int
+_cond_check_need_init(pthread_cond_t *cond)
+{
+ int result = 0;
+
+ /*
+ * The following guarded test is specifically for statically
+ * initialised condition variables (via PTHREAD_COND_INITIALIZER).
+ *
+ * Note that by not providing this synchronisation we risk
+ * introducing race conditions into applications which are
+ * correctly written.
+ *
+ * Approach
+ * --------
+ * We know that static condition variables 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-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).
+ * 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)
+ {
+ result = pthread_cond_init(cond, NULL);
+ }
+
+ LeaveCriticalSection(&_pthread_cond_test_init_lock);
+
+ return(result);
+}
+
+
int
pthread_condattr_init (pthread_condattr_t * attr)
/*
@@ -281,7 +342,19 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
*/
{
int result = EAGAIN;
- pthread_cond_t cv = NULL;
+
+ 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))
@@ -295,37 +368,34 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
goto FAIL0;
}
- cv = (pthread_cond_t) calloc (1, sizeof (*cv));
-
- if (cv == NULL)
- {
- result = ENOMEM;
- goto FAIL0;
- }
-
- cv->waiters = 0;
- cv->wasBroadcast = FALSE;
+ cond->waiters = 0;
+ cond->wasBroadcast = FALSE;
- if (_pthread_sem_init (&(cv->sema), 0, 1) != 0)
+ if (_pthread_sem_init (&(cond->sema), 0, 0) != 0)
{
goto FAIL0;
}
- if (pthread_mutex_init (&(cv->waitersLock), NULL) != 0)
+ if (pthread_mutex_init (&(cond->waitersLock), NULL) != 0)
{
goto FAIL1;
}
- cv->waitersDone = CreateEvent (
+ cond->waitersDone = CreateEvent (
0,
(int) FALSE, /* manualReset */
(int) FALSE, /* setSignaled */
NULL);
- if (cv->waitersDone == NULL)
+ if (cond->waitersDone == NULL)
{
goto FAIL2;
}
+ cond->staticinit = 0;
+
+ /* Mark as valid. */
+ cond->valid = 1;
+
result = 0;
goto DONE;
@@ -336,16 +406,13 @@ pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
* -------------
*/
FAIL2:
- (void) pthread_mutex_destroy (&(cv->waitersLock));
+ (void) pthread_mutex_destroy (&(cond->waitersLock));
FAIL1:
- (void) _pthread_sem_destroy (&(cv->sema));
- free (cv);
- cv = NULL;
+ (void) _pthread_sem_destroy (&(cond->sema));
FAIL0:
DONE:
- *cond = cv;
return (result);
} /* pthread_cond_init */
@@ -380,21 +447,26 @@ pthread_cond_destroy (pthread_cond_t * cond)
*/
{
int result = 0;
- pthread_cond_t cv;
- if (cond != NULL && *cond != NULL)
+ /*
+ * Assuming any race condition here is harmless.
+ */
+ if (cond == NULL || cond->valid == 0 || cond->staticinit == 1)
{
- cv = *cond;
-
- (void) _pthread_sem_destroy (&(cv->sema));
- (void) pthread_mutex_destroy (&(cv->waitersLock));
- (void) CloseHandle (cv->waitersDone);
-
- free (cv);
+ return EINVAL;
+ }
- *cond = NULL;
+ if (cond->waiters > 0)
+ {
+ return EBUSY;
}
+ (void) _pthread_sem_destroy (&(cond->sema));
+ (void) pthread_mutex_destroy (&(cond->waitersLock));
+ (void) CloseHandle (cond->waitersDone);
+
+ cond->valid = 0;
+
return (result);
}
@@ -404,15 +476,24 @@ cond_timedwait (pthread_cond_t * cond,
const struct timespec *abstime)
{
int result = 0;
- pthread_cond_t cv;
- int lastWaiter;
+ int internal_result = 0;
+ int lastWaiter = FALSE;
- 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'
+ * again inside the guarded section of _cond_check_need_init()
+ * to avoid race conditions.
+ */
+ if (cond->staticinit == 1)
+ {
+ result = _cond_check_need_init(cond);
+ }
/*
- * OK to increment cv->waiters because the caller locked 'mutex'
+ * OK to increment cond->waiters because the caller locked 'mutex'
*/
- cv->waiters++;
+ cond->waiters++;
/*
* We keep the lock held just long enough to increment the count of
@@ -433,30 +514,30 @@ cond_timedwait (pthread_cond_t * cond,
* hence providing the
* mechanism for making pthread_cond_wait a cancellation
* point. We use the cleanup mechanism to ensure we
- * re-lock the mutex if we are cancelled.
+ * re-lock the mutex if we are cancelled.
*/
pthread_cleanup_push (pthread_mutex_lock, mutex);
- result = _pthread_sem_timedwait (&(cv->sema), abstime);
+ result = _pthread_sem_timedwait (&(cond->sema), abstime);
pthread_cleanup_pop (0);
}
- if ((result = pthread_mutex_lock (&(cv->waitersLock))) == 0)
+ if ((internal_result = pthread_mutex_lock (&(cond->waitersLock))) == 0)
{
/*
* By making the waiter responsible for decrementing
* its count we don't have to worry about having an internal
* mutex.
*/
- cv->waiters--;
+ cond->waiters--;
- lastWaiter = cv->wasBroadcast && (cv->waiters == 0);
+ lastWaiter = cond->wasBroadcast && (cond->waiters == 0);
- result = pthread_mutex_unlock (&(cv->waitersLock));
+ internal_result = pthread_mutex_unlock (&(cond->waitersLock));
}
- if (result == 0)
+ if (result == 0 && internal_result == 0)
{
if (lastWaiter)
{
@@ -464,7 +545,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 (cv->waitersDone))
+ if (!SetEvent (cond->waitersDone))
{
result = EINVAL;
}
@@ -646,15 +727,26 @@ pthread_cond_signal (pthread_cond_t * cond)
*/
{
int result = 0;
- pthread_cond_t cv = *cond;
+
+ if (cond == NULL || cond->valid == 0)
+ {
+ return EINVAL;
+ }
/*
- * If there aren't any waiters, then this is a no-op.
+ * No-op if the CV is static and hasn't been initialised yet.
*/
- if (cv->waiters > 0)
+ if (cond->staticinit == 1)
{
+ return 0;
+ }
- result = _pthread_sem_post (&(cv->sema));
+ /*
+ * If there aren't any waiters, then this is a no-op.
+ */
+ if (cond->waiters > 0)
+ {
+ result = _pthread_sem_post (&(cond->sema));
}
return (result);
@@ -700,27 +792,39 @@ pthread_cond_broadcast (pthread_cond_t * cond)
*/
{
int result = 0;
- pthread_cond_t cv = *cond;
int i;
- cv->wasBroadcast = TRUE;
+ if (cond == NULL || cond->valid == 0)
+ {
+ return EINVAL;
+ }
+
+ cond->wasBroadcast = TRUE;
+
+ /*
+ * No-op if the CV is static and hasn't been initialised yet.
+ */
+ if (cond->staticinit == 1)
+ {
+ return 0;
+ }
/*
* Wake up all waiters
*/
- for (i = cv->waiters; i > 0 && result == 0; i--)
+ for (i = cond->waiters; i > 0 && result == 0; i--)
{
- result = _pthread_sem_post (&(cv->sema));
+ result = _pthread_sem_post (&(cond->sema));
}
- if (cv->waiters > 0 && result == 0)
+ if (cond->waiters > 0 && result == 0)
{
/*
* Wait for all the awakened threads to acquire their part of
* the counting semaphore
*/
- if (WaitForSingleObject (cv->waitersDone, INFINITE) !=
+ if (WaitForSingleObject (cond->waitersDone, INFINITE) !=
WAIT_OBJECT_0)
{
diff --git a/global.c b/global.c
index a8bd69a..ecc3d27 100644
--- a/global.c
+++ b/global.c
@@ -20,5 +20,11 @@ pthread_key_t _pthread_cleanupKey = NULL;
*/
CRITICAL_SECTION _pthread_mutex_test_init_lock;
+/*
+ * Global lock for testing internal state of PTHREAD_COND_INITIALIZER
+ * created condition variables.
+ */
+CRITICAL_SECTION _pthread_cond_test_init_lock;
+
diff --git a/implement.h b/implement.h
index 2103027..4ee33a6 100644
--- a/implement.h
+++ b/implement.h
@@ -150,6 +150,7 @@ extern int _pthread_processInitialized;
extern pthread_key_t _pthread_selfThreadKey;
extern pthread_key_t _pthread_cleanupKey;
extern CRITICAL_SECTION _pthread_mutex_test_init_lock;
+extern CRITICAL_SECTION _pthread_cond_test_init_lock;
#include <pthread.h>
diff --git a/misc.c b/misc.c
index 025653c..df7af10 100644
--- a/misc.c
+++ b/misc.c
@@ -213,8 +213,12 @@ CancelableWait (HANDLE waitHandle, DWORD timeout)
DWORD nHandles = 1;
DWORD status;
- handles[0] = waitHandle;
-
+ /*
+ * 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.
+ */
if ((self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey))
!= NULL)
{
@@ -224,16 +228,14 @@ CancelableWait (HANDLE waitHandle, DWORD timeout)
if (self->cancelState == PTHREAD_CANCEL_ENABLE)
{
- if ((handles[1] = self->cancelEvent) != NULL)
+ if ((handles[0] = self->cancelEvent) != NULL)
{
nHandles++;
}
}
}
- else
- {
- handles[1] = NULL;
- }
+
+ handles[nHandles - 1] = waitHandle;
status = WaitForMultipleObjects (
nHandles,
@@ -241,41 +243,36 @@ 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)
+ else if (status >= WAIT_ABANDONED_0 && status <= WAIT_ABANDONED_0 + nHandles - 1)
{
+ /*
+ * The waitHandle was a mutex object that was abandoned.
+ */
result = EINVAL;
}
else
{
/*
- * Either got the mutex or the cancel event
- * was signaled
+ * Either got the object or the cancel event
+ * was signaled, or both in which case the cancel
+ * event will be acted on.
*/
- switch (status - WAIT_OBJECT_0)
+ switch (status - WAIT_OBJECT_0 + 2 - nHandles)
{
case 0:
/*
- * Got the mutex
- */
- result = 0;
- break;
-
- case 1:
- /*
- * Got cancel request
+ * Got cancel request.
*/
- ResetEvent (handles[1]);
+ ResetEvent (handles[0]);
if (self != NULL && !self->implicit)
{
@@ -307,8 +304,15 @@ CancelableWait (HANDLE waitHandle, DWORD timeout)
#endif /* _MSC_VER */
}
- /* Should never get to here. */
- result = EINVAL;
+ /* Should never get to here. */
+ result = EINVAL;
+ break;
+
+ case 1:
+ /*
+ * Got the object.
+ */
+ result = 0;
break;
default:
diff --git a/mutex.c b/mutex.c
index 578617d..b88eac4 100644
--- a/mutex.c
+++ b/mutex.c
@@ -451,7 +451,7 @@ pthread_mutex_lock(pthread_mutex_t *mutex)
* again inside the guarded section of _mutex_check_need_init()
* to avoid race conditions.
*/
- if (mutex->staticinit)
+ if (mutex->staticinit == 1)
{
result = _mutex_check_need_init(mutex);
}
diff --git a/private.c b/private.c
index 88a71ee..56062a1 100644
--- a/private.c
+++ b/private.c
@@ -56,9 +56,10 @@ _pthread_processInitialize (void)
}
/*
- * Set up the global mutex test and init check lock.
+ * Set up the global test and init check locks.
*/
InitializeCriticalSection(&_pthread_mutex_test_init_lock);
+ InitializeCriticalSection(&_pthread_cond_test_init_lock);
return (_pthread_processInitialized);
@@ -111,9 +112,10 @@ _pthread_processTerminate (void)
}
/*
- * Destroy up the global mutex test and init check lock.
+ * Destroy the global test and init check locks.
*/
DeleteCriticalSection(&_pthread_mutex_test_init_lock);
+ DeleteCriticalSection(&_pthread_cond_test_init_lock);
_pthread_processInitialized = FALSE;
}
diff --git a/pthread.h b/pthread.h
index e17a2c6..da40f48 100644
--- a/pthread.h
+++ b/pthread.h
@@ -421,7 +421,7 @@ extern "C"
typedef struct pthread_key_t_ *pthread_key_t;
typedef struct pthread_mutex_t_ pthread_mutex_t;
typedef struct pthread_mutexattr_t_ *pthread_mutexattr_t;
- typedef struct pthread_cond_t_ *pthread_cond_t;
+ typedef struct pthread_cond_t_ pthread_cond_t;
typedef struct pthread_condattr_t_ *pthread_condattr_t;
@@ -458,20 +458,6 @@ extern "C"
#define PTHREAD_CANCEL_DEFERRED 1
/*
- * ====================
- * ====================
- * Mutex
- * ====================
- * ====================
- */
-
-/*
- *
- */
-#define PTHREAD_MUTEX_INITIALIZER { 1, 1, 0, {NULL} }
-
-
-/*
* pthread_mutexattr_{get,set}pshared
* pthread_condattr_{get,set}pshared
*/
@@ -500,20 +486,11 @@ struct pthread_once_t_
/* to zero executes the user function */
};
-/*
- * ====================
- * ====================
- * Condition Variable
- * ====================
- * ====================
- */
-#define PTHREAD_COND_INITIALIZER { {0, 0}, 0, PTHREAD_MUTEX_INITIALIZER }
-
/*
* ====================
* ====================
- * Opaque Structure Definitions
+ * Structure Definitions
* ====================
* ====================
*/
@@ -587,9 +564,19 @@ struct pthread_attr_t_ {
};
+/*
+ * ====================
+ * ====================
+ * Mutex
+ * ====================
+ * ====================
+ */
+
+#define PTHREAD_MUTEX_INITIALIZER { 1, 1 /* Remaining are all 0 */ }
+
struct pthread_mutex_t_ {
- int staticinit;
- int valid;
+ int staticinit; /* Needs implicit init if 1. */
+ int valid; /* Not destroyed if 1. */
HANDLE mutex;
CRITICAL_SECTION cs;
};
@@ -609,9 +596,21 @@ struct pthread_key_t_ {
};
+/*
+ * ====================
+ * ====================
+ * Condition Variable
+ * ====================
+ * ====================
+ */
+
+#define PTHREAD_COND_INITIALIZER { 1, 1 /* Remaining are all 0 */ }
+
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/semaphore.c b/semaphore.c
index f3bd5c9..5f7d083 100644
--- a/semaphore.c
+++ b/semaphore.c
@@ -267,6 +267,11 @@ _pthread_sem_timedwait (_pthread_sem_t * sem, const struct timespec * abstime)
const DWORD MILLISEC_PER_SEC = 1000;
DWORD milliseconds;
+ if (sem == NULL)
+ {
+ return EINVAL;
+ }
+
if (abstime == NULL)
{
milliseconds = INFINITE;
@@ -286,10 +291,7 @@ _pthread_sem_timedwait (_pthread_sem_t * sem, const struct timespec * abstime)
currSysTime.millitm;
}
- return ((sem == NULL)
- ? EINVAL
- : pthreadCancelableTimedWait (*sem, milliseconds)
- );
+ return (pthreadCancelableTimedWait (*sem, milliseconds));
} /* _pthread_sem_timedwait */
diff --git a/tests/Makefile b/tests/Makefile
index 14228b7..dcbb7e0 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,69 +1,70 @@
-# Makefile for the pthreads test suite.
-# If all of the .pass files can be created, the test suite has passed.
-
-
-CP = copy
-RM = erase
-MKDIR = mkdir
-TOUCH = echo Passed >
-ECHO = @echo
-
-#
-# Mingw32
-#
-CC = gcc
-CFLAGS = -g -O2 -UNDEBUG -Wall -o $@ $^
-BUILD_DIR = ..
-INCLUDES = -I./include
-LIBS = ./lib/libpthread32.a
-
-##
-## MSVC
-##
-#CC = cl
-#CFLAGS = /W3 /MT /nologo /Yd /Zi /Fe$@ $^
-#BUILD_DIR = ..
-#INCLUDES = -I.\include
-#LIBS = .\lib\pthread.lib
-
-HDR = .\include\pthread.h
-LIB = .\lib\libpthread32.a
-DLL = pthread.dll
-
-# If a test case returns a non-zero exit code to the shell, make will
-# stop.
-
-TESTS = count1 create1 equal1 exit1 exit2 exit3 \
- join1 eyal1 mutex1 mutex2 mutex3 \
- once1 self1 self2 condvar1 condvar2 condvar3 condvar4 tsd1
-
-PASSES = $(TESTS:%=%.pass)
-
-all: $(PASSES)
- @ $(ECHO) ALL TESTS PASSED! Congratulations!
-
-%.pass: %.exe $(LIB) $(DLL) $(HDR)
- $*
- @$(ECHO) Passed
- @ $(TOUCH) $@
-
-%.exe: %.c
- @ $(CC) $(CFLAGS) $(INCLUDES) $(LIBS)
-
-$(LIB):
- @- $(MKDIR) .\lib
- @ $(CP) $(BUILD_DIR)\$@ .\$@
-
-$(HDR):
- @- $(MKDIR) .\include
- @ $(CP) $(BUILD_DIR)\$@ .\$@
-
-$(DLL):
- @ $(CP) $(BUILD_DIR)\$@ .\$@
-
-clean:
- - $(RM) *.dll
- - $(RM) $(LIB)
- - $(RM) $(HDR)
- - $(RM) *.exe
- - $(RM) *.pass
+# Makefile for the pthreads test suite.
+# If all of the .pass files can be created, the test suite has passed.
+
+
+CP = copy
+RM = erase
+MKDIR = mkdir
+TOUCH = echo Passed >
+ECHO = @echo
+
+#
+# Mingw32
+#
+CC = gcc
+CFLAGS = -g -O2 -UNDEBUG -Wall -o $@ $^
+BUILD_DIR = ..
+INCLUDES = -I.
+LIBS = ./libpthread32.a
+
+##
+## MSVC
+##
+#CC = cl
+#CFLAGS = /W3 /MT /nologo /Yd /Zi /Fe$@ $^
+#BUILD_DIR = ..
+#INCLUDES = -I.
+#LIBS = pthread.lib
+
+HDR = pthread.h
+LIB = libpthread32.a
+DLL = pthread.dll
+
+# If a test case returns a non-zero exit code to the shell, make will
+# stop.
+
+TESTS = count1 create1 equal1 exit1 exit2 exit3 \
+ join1 mutex1 mutex2 mutex3 \
+ once1 self1 self2 condvar1 condvar2 condvar3 condvar4 tsd1
+
+PASSES = $(TESTS:%=%.pass)
+
+all: $(PASSES)
+ @ $(ECHO) ALL TESTS PASSED! Congratulations!
+
+%.pass: %.exe $(LIB) $(DLL) $(HDR)
+ $*
+ @$(ECHO) Passed
+ @ $(TOUCH) $@
+
+%.exe: %.c
+ @ $(CC) $(CFLAGS) $(INCLUDES) $(LIBS)
+
+$(LIB):
+ @ $(ECHO) Copying the library
+ @ $(CP) $(BUILD_DIR)\$@ .
+
+$(HDR):
+ @ $(ECHO) Copying the header file
+ @ $(CP) $(BUILD_DIR)\$@ .
+
+$(DLL):
+ @ $(ECHO) Copying the DLL
+ @ $(CP) $(BUILD_DIR)\$@ .
+
+clean:
+ - $(RM) *.dll
+ - $(RM) $(LIB)
+ - $(RM) $(HDR)
+ - $(RM) *.exe
+ - $(RM) *.pass
diff --git a/tests/condvar2.c b/tests/condvar2.c
index 107ed8f..66f3d7b 100644
--- a/tests/condvar2.c
+++ b/tests/condvar2.c
@@ -1,73 +1,83 @@
-/*
- * File: condvar1.c
- *
- * Test Synopsis:
- * - Test timed wait on a CV.
- *
- * Test Method (Validation or Falsification):
- * - Validation
- *
- * Requirements Tested:
- * -
- *
- * Features Tested:
- * -
- *
- * Cases Tested:
- * -
- *
- * Description:
- * - Because the CV is never signaled, we expect the wait to time out.
- *
- * Environment:
- * -
- *
- * Input:
- * - None.
- *
- * Output:
- * - File name, Line number, and failed expression on failure.
- * - No output on success.
- *
- * Assumptions:
- * -
- *
- * Pass Criteria:
- * - pthread_cond_timedwait returns ETIMEDOUT.
- * - Process returns zero exit status.
- *
- * Fail Criteria:
- * - pthread_cond_timedwait does not return ETIMEDOUT.
- * - Process returns non-zero exit status.
- */
-
-#include "test.h"
-
-pthread_cond_t cv;
-pthread_mutex_t mutex;
-
-int
-main()
-{
- struct timespec abstime = { 0, 0 };
- struct timeval curtime;
-
- assert(pthread_cond_init(&cv, NULL) == 0);
-
- assert(pthread_mutex_init(&mutex) == 0);
-
- assert(pthread_mutex_lock(&mutex) == 0);
-
- assert(gettimeofday(&curtime, NULL) == 0);
-
- abstime.tv_sec = curtime.tv_sec + 5;
-
- assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == ETIMEDOUT);
-
- assert(pthread_mutex_unlock(&mutex) == 0);
-
- assert(pthread_cond_destroy(&cv) == 0);
-
- return 0;
-}
-
+/*
+ * File: condvar1.c
+ *
+ * Test Synopsis:
+ * - Test timed wait on a CV.
+ *
+ * Test Method (Validation or Falsification):
+ * - Validation
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * - Because the CV is never signaled, we expect the wait to time out.
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * -
+ *
+ * Pass Criteria:
+ * - pthread_cond_timedwait returns ETIMEDOUT.
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - pthread_cond_timedwait does not return ETIMEDOUT.
+ * - Process returns non-zero exit status.
+ */
+
+#include "test.h"
+#include <sys/timeb.h>
+
+pthread_cond_t cv;
+pthread_mutex_t mutex;
+
+int
+main()
+{
+ struct timespec abstime = { 0, 0 };
+#if defined(__MINGW32__)
+ struct timeb currSysTime;
+#else
+ struct _timeb currSysTime;
+#endif
+ const DWORD NANOSEC_PER_MILLISEC = 1000000;
+
+ 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;
+
+ abstime.tv_sec += 5;
+
+ assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == ETIMEDOUT);
+
+ assert(pthread_mutex_unlock(&mutex) == 0);
+
+ assert(pthread_cond_destroy(&cv) == 0);
+
+ return 0;
+}
+
diff --git a/tests/condvar3.c b/tests/condvar3.c
index 97a17cf..8dc56af 100644
--- a/tests/condvar3.c
+++ b/tests/condvar3.c
@@ -1,103 +1,109 @@
-/*
- * File: condvar1.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"
-
-typedef struct cvthing_t_ cvthing_t;
-
-struct cvthing_t_ {
- pthread_cond_t notbusy;
- pthread_mutex_t lock;
-};
-
-static cvthing_t cvthing;
-
-static enum {
- NUMTHREADS = 2 /* Including the primary thread. */
-};
-
-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 };
- struct timeval curtime;
-
- assert((t[0] = pthread_self()) != NULL);
-
- assert(pthread_cond_init(&cvthing, NULL) == 0);
-
- assert(pthread_mutex_init(&cvthing.lock) == 0);
-
- assert(pthread_mutex_lock(&cvthing.lock) == 0);
-
- assert(gettimeofday(&curtime, NULL) == 0);
-
- abstime.tv_sec = curtime.tv_sec + 5;
-
- assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);
-
- assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
-
- assert(pthread_mutex_unlock(&cvthing.lock) == 0);
-
- assert(pthread_cond_destroy(&cvthing) == 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 867746b..4c5fc38 100644
--- a/tests/condvar4.c
+++ b/tests/condvar4.c
@@ -1,106 +1,121 @@
-/*
- * 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"
-
-typedef struct cvthing_t_ cvthing_t;
-
-struct cvthing_t_ {
- pthread_cond_t notbusy;
- pthread_mutex_t lock;
- int busy;
- int count;
-};
-
-static cvthing_t cvthing = {
- PTHREAD_MUTEX_INITIALIZER,
- PTHREAD_COND_INITIALIZER,
-};
-
-static 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];
- int result[NUMTHREADS];
- struct timespec abstime = { 0, 0 };
- struct timeval curtime;
-
- assert((t[0] = pthread_self()) != NULL);
-
- assert(pthread_mutex_lock(&cvthing.lock) == 0);
-
- gettimeofday(&curtime, NULL);
- abstime.tv_sec = curtime.tv_sec + 5;
-
- assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == ETIMEDOUT);
-
- assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);
-
- gettimeofday(&curtime, NULL);
- abstime.tv_sec = curtime.tv_sec + 10;
-
- assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
-
- assert(pthread_mutex_unlock(&cvthing.lock) == 0);
-
- assert(pthread_cond_destroy(&cvthing) == 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(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;
+}