summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrpj <rpj>2004-09-13 04:32:16 +0000
committerrpj <rpj>2004-09-13 04:32:16 +0000
commit531ca4db4794aab863a898b4d079ccd59b424b25 (patch)
tree0fb6d71aef115a8a01cd0252363f369517deed3c
parent4b0d69122798d07ac700941d3b649f1653750ce2 (diff)
Clarify behaviour and remove some redundant code - see ChangeLogs
-rw-r--r--ChangeLog124
-rw-r--r--create.c48
-rw-r--r--implement.h15
-rw-r--r--pthread.h26
-rw-r--r--pthread_barrier_wait.c20
-rw-r--r--pthread_join.c163
-rw-r--r--pthread_once.c12
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/GNUmakefile3
-rw-r--r--tests/Makefile3
10 files changed, 267 insertions, 151 deletions
diff --git a/ChangeLog b/ChangeLog
index fbb0aee..7ce169c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,59 +1,103 @@
-2004-07-01 Anuj Goyal <anuj.goyal at gmail.com>
+2004-09-09 Tristan Savatier <tristan at mpegtv.com>
+ * 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.]
- * builddmc.bat: New; Windows bat file to build the library.
- * config.h (__DMC__): Support for Digital Mars compiler.
- * create.c (__DMC__): Likewise.
- * pthread_exit.c (__DMC__): Likewise.
- * pthread_join.c (__DMC__): Likewise.
- * ptw32_threadDestroy.c (__DMC__): Likewise.
- * ptw32_threadStart.c (__DMC__): Likewise.
- * ptw32_throw.c (__DMC__): Likewise.
+2004-09-09 Ross Johnson <rpj at callisto.canberra.edu.au>
-2004-06-29 Anuj Goyal <anuj.goyal at gmail.com>
+ * pthread.h: Declare additional types as volatile.
- * pthread.h (__DMC__): Initial support for Digital Mars compiler.
+2004-08-27 Ross Johnson <rpj at callisto.canberra.edu.au>
-2004-06-29 Will Bryant <will.bryant at ecosm.com>
+ * pthread_barrier_wait.c (pthread_barrier_wait): Remove excessive code
+ by substituting the internal non-cancelable version of sem_wait
+ (ptw32_semwait).
- * README.Borland: New; description of Borland changes.
- * Bmakefile: New makefile for the Borland make utility.
- * ptw32_InterlockedCompareExchange.c:
- Add Borland compatible asm code.
+2004-08-25 Ross Johnson <rpj at callisto.canberra.edu.au>
-2004-06-26 Jason Bard <BardJA at Npt.NUWC.Navy.Mil>
+ * pthread_join.c (pthread_join): Rewrite and re-order the conditional
+ tests in an attempt to improve efficiency and remove a race
+ condition.
- * pthread.h (HAVE_STRUCT_TIMESPEC): If undefined, define it
- to avoid timespec struct redefined errors elsewhere in an
- application.
+2004-08-23 Ross Johnson <rpj at callisto.canberra.edu.au>
-2004-06-21 Ross Johnson <rpj at callisto.canberra.edu.au>
+ * create.c (pthread_create): Don't create a thread if the thread
+ id pointer location (first arg) is inaccessible. A memory
+ protection fault will result if the thread id arg isn't an accessible
+ location. This is consistent with GNU/Linux but different to
+ Solaris or MKS (and possibly others), which accept NULL as meaning
+ 'don't return the created thread's ID'. Applications that run
+ using pthreads-win32 will run on all other POSIX threads
+ implementations, at least w.r.t. this feature.
- * pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER): Mutex
- initialiser added for compatibility with Linux threads and
- others; currently not included in SUSV3.
- * pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER): Likewise.
- * pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP): Likewise.
- * pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP): Likewise.
+ It was decided not to copy the Solaris et al behaviour because,
+ although it would have simplified some application porting (but only
+ from Solaris to Windows), the feature is not technically necessary,
+ and the alternative segfault behaviour helps avoid buggy application
+ code.
- * ptw32_mutex_check_need_init.c (ptw32_mutex_check_need_init):
- Add new initialisers.
+2004-07-01 Anuj Goyal <anuj.goyal at gmail.com>
- * pthread_mutex_lock.c (pthread_mutex_lock): Check for new
- initialisers.
- * pthread_mutex_trylock.c (pthread_mutex_trylock): Likewise.
- * pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise.
- * pthread_mutex_unlock.c (pthread_mutex_unlock): Likewise.
- * pthread_mutex_destroy.c (pthread_mutex_destroy): Likewise.
+ * builddmc.bat: New; Windows bat file to build the library.
+ * config.h (__DMC__): Support for Digital Mars compiler.
+ * create.c (__DMC__): Likewise.
+ * pthread_exit.c (__DMC__): Likewise.
+ * pthread_join.c (__DMC__): Likewise.
+ * ptw32_threadDestroy.c (__DMC__): Likewise.
+ * ptw32_threadStart.c (__DMC__): Likewise.
+ * ptw32_throw.c (__DMC__): Likewise.
-2004-05-20 Ross Johnson <rpj at callisto.canberra.edu.au>
+2004-06-29 Anuj Goyal <anuj.goyal at gmail.com>
- * README.NONPORTABLE: Document pthread_win32_test_features_np().
- * FAQ: Update various answers.
+ * pthread.h (__DMC__): Initial support for Digital Mars compiler.
+
+2004-06-29 Will Bryant <will.bryant at ecosm.com>
+
+ * README.Borland: New; description of Borland changes.
+ * Bmakefile: New makefile for the Borland make utility.
+ * ptw32_InterlockedCompareExchange.c:
+ Add Borland compatible asm code.
+
+2004-06-26 Jason Bard <BardJA at Npt.NUWC.Navy.Mil>
+
+ * pthread.h (HAVE_STRUCT_TIMESPEC): If undefined, define it
+ to avoid timespec struct redefined errors elsewhere in an
+ application.
+
+2004-06-21 Ross Johnson <rpj at callisto.canberra.edu.au>
+
+ * pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER): Mutex
+ initialiser added for compatibility with Linux threads and
+ others; currently not included in SUSV3.
+ * pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER): Likewise.
+ * pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP): Likewise.
+ * pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP): Likewise.
+
+ * ptw32_mutex_check_need_init.c (ptw32_mutex_check_need_init):
+ Add new initialisers.
+
+ * pthread_mutex_lock.c (pthread_mutex_lock): Check for new
+ initialisers.
+ * pthread_mutex_trylock.c (pthread_mutex_trylock): Likewise.
+ * pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise.
+ * pthread_mutex_unlock.c (pthread_mutex_unlock): Likewise.
+ * pthread_mutex_destroy.c (pthread_mutex_destroy): Likewise.
+
+2004-05-20 Ross Johnson <rpj at callisto.canberra.edu.au>
+
+ * README.NONPORTABLE: Document pthread_win32_test_features_np().
+ * FAQ: Update various answers.
2004-05-19 Ross Johnson <rpj at callisto.canberra.edu.au>
- * Makefile: Don't define _WIN32_WINNT on compiler command line.
- * GNUmakefile: Likewise.
+ * Makefile: Don't define _WIN32_WINNT on compiler command line.
+ * GNUmakefile: Likewise.
2004-05-16 Ross Johnson <rpj at callisto.canberra.edu.au>
diff --git a/create.c b/create.c
index 2ef4597..048088b 100644
--- a/create.c
+++ b/create.c
@@ -49,7 +49,10 @@ pthread_create (pthread_t * tid,
* ------------------------------------------------------
* DOCPUBLIC
* This function creates a thread running the start function,
- * passing it the parameter value, 'arg'.
+ * passing it the parameter value, 'arg'. The 'attr'
+ * argument specifies optional creation attributes.
+ * The identity of the new thread is returned
+ * via 'tid', which should not be NULL.
*
* PARAMETERS
* tid
@@ -69,8 +72,8 @@ pthread_create (pthread_t * tid,
* This function creates a thread running the start function,
* passing it the parameter value, 'arg'. The 'attr'
* argument specifies optional creation attributes.
- * The thread is identity of the new thread is returned
- * as 'tid'
+ * The identity of the new thread is returned
+ * via 'tid', which should not be the NULL pointer.
*
* RESULTS
* 0 successfully created thread,
@@ -81,6 +84,7 @@ pthread_create (pthread_t * tid,
*/
{
pthread_t thread;
+ register pthread_attr_t a;
HANDLE threadH = 0;
int result = EAGAIN;
int run = PTW32_TRUE;
@@ -88,6 +92,23 @@ pthread_create (pthread_t * tid,
long stackSize;
int priority;
+ /*
+ * Before doing anything, check that tid can be stored through
+ * without invoking a memory protection error (segfault).
+ * Make sure that the assignment below can't be optimised out by the compiler.
+ * This is assured by conditionally assigning *tid again at the end.
+ */
+ *tid = NULL;
+
+ if (attr != NULL)
+ {
+ a = *attr;
+ }
+ else
+ {
+ a = NULL;
+ }
+
if ((thread = ptw32_new ()) == NULL)
{
goto FAIL0;
@@ -104,15 +125,15 @@ pthread_create (pthread_t * tid,
parms->start = start;
parms->arg = arg;
- if (attr != NULL && *attr != NULL)
+ if (a != NULL)
{
- stackSize = (*attr)->stacksize;
- thread->detachState = (*attr)->detachstate;
- priority = (*attr)->param.sched_priority;
+ stackSize = a->stacksize;
+ thread->detachState = a->detachstate;
+ priority = a->param.sched_priority;
#if HAVE_SIGSET_T
- thread->sigmask = (*attr)->sigmask;
+ thread->sigmask = a->sigmask;
#endif /* HAVE_SIGSET_T */
@@ -133,7 +154,7 @@ pthread_create (pthread_t * tid,
* an 'attr' arg to pthread_create() is equivalent to defaulting to
* PTHREAD_EXPLICIT_SCHED and priority THREAD_PRIORITY_NORMAL.
*/
- if (PTHREAD_INHERIT_SCHED == (*attr)->inheritsched)
+ if (PTHREAD_INHERIT_SCHED == a->inheritsched)
{
/*
* If the thread that called pthread_create() is a Win32 thread
@@ -181,7 +202,7 @@ pthread_create (pthread_t * tid,
if (threadH != 0)
{
- if (attr != NULL && *attr != NULL)
+ if (a != NULL)
{
(void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority);
}
@@ -222,7 +243,7 @@ pthread_create (pthread_t * tid,
SuspendThread (threadH);
}
- if (attr != NULL && *attr != NULL)
+ if (a != NULL)
{
(void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority);
}
@@ -256,7 +277,10 @@ FAIL0:
free (parms);
}
}
- *tid = thread;
+ else
+ {
+ *tid = thread;
+ }
#ifdef _UWIN
if (result == 0)
diff --git a/implement.h b/implement.h
index 4ce91cd..95308f5 100644
--- a/implement.h
+++ b/implement.h
@@ -107,20 +107,6 @@ typedef enum
PThreadState;
-typedef enum
-{
- /*
- * This enumeration represents the reason why a thread has
- * terminated/is terminating.
- */
- PThreadDemisePeaceful = 0, /* Death due natural causes */
- PThreadDemiseCancelled, /* Death due to user cancel */
- PThreadDemiseException, /* Death due to unhandled */
- /* exception */
- PThreadDemiseNotDead /* I'm not dead! */
-}
-PThreadDemise;
-
struct pthread_t_
{
#ifdef _UWIN
@@ -130,7 +116,6 @@ struct pthread_t_
HANDLE threadH; /* POSIX thread is invalid if threadH == 0 */
pthread_t prevReuse; /* Links threads on reuse stack */
PThreadState state;
- PThreadDemise demise;
void *exitStatus;
void *parms;
int ptErrno;
diff --git a/pthread.h b/pthread.h
index 6b677a8..9975eb1 100644
--- a/pthread.h
+++ b/pthread.h
@@ -519,20 +519,20 @@ extern "C"
#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX
# include <sys/types.h>
#else
-typedef struct pthread_t_ *pthread_t;
-typedef struct pthread_attr_t_ *pthread_attr_t;
+typedef struct pthread_t_ * volatile pthread_t;
+typedef struct pthread_attr_t_ * volatile 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_mutexattr_t_ *pthread_mutexattr_t;
-typedef struct pthread_cond_t_ *pthread_cond_t;
-typedef struct pthread_condattr_t_ *pthread_condattr_t;
+typedef struct pthread_key_t_ * volatile pthread_key_t;
+typedef struct pthread_mutex_t_ * volatile pthread_mutex_t;
+typedef struct pthread_mutexattr_t_ * volatile pthread_mutexattr_t;
+typedef struct pthread_cond_t_ * volatile pthread_cond_t;
+typedef struct pthread_condattr_t_ * volatile pthread_condattr_t;
#endif
-typedef struct pthread_rwlock_t_ *pthread_rwlock_t;
-typedef struct pthread_rwlockattr_t_ *pthread_rwlockattr_t;
-typedef struct pthread_spinlock_t_ *pthread_spinlock_t;
-typedef struct pthread_barrier_t_ *pthread_barrier_t;
-typedef struct pthread_barrierattr_t_ *pthread_barrierattr_t;
+typedef struct pthread_rwlock_t_ * volatile pthread_rwlock_t;
+typedef struct pthread_rwlockattr_t_ * volatile pthread_rwlockattr_t;
+typedef struct pthread_spinlock_t_ * volatile pthread_spinlock_t;
+typedef struct pthread_barrier_t_ * volatile pthread_barrier_t;
+typedef struct pthread_barrierattr_t_ * volatile pthread_barrierattr_t;
/*
* ====================
@@ -607,7 +607,7 @@ enum {
struct pthread_once_t_
{
- int done; /* indicates if user function executed */
+ volatile int done; /* indicates if user function executed */
long started; /* First thread to increment this value */
/* to zero executes the user function */
};
diff --git a/pthread_barrier_wait.c b/pthread_barrier_wait.c
index b067d66..200a02d 100644
--- a/pthread_barrier_wait.c
+++ b/pthread_barrier_wait.c
@@ -73,26 +73,10 @@ pthread_barrier_wait (pthread_barrier_t * barrier)
}
else
{
- BOOL switchCancelState;
- int oldCancelState;
- pthread_t self = pthread_self ();
-
/*
- * This routine is not a cancelation point, so temporarily
- * prevent sem_wait() from being one.
- * PTHREAD_CANCEL_ASYNCHRONOUS threads can still be canceled.
+ * Use the non-cancelable version of sem_wait().
*/
- switchCancelState = (self->cancelType == PTHREAD_CANCEL_DEFERRED &&
- 0 ==
- pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,
- &oldCancelState));
-
- result = sem_wait (&(b->semBarrierBreeched[step]));
-
- if (switchCancelState)
- {
- (void) pthread_setcancelstate (oldCancelState, NULL);
- }
+ result = ptw32_semwait (&(b->semBarrierBreeched[step]));
}
/*
diff --git a/pthread_join.c b/pthread_join.c
index 97cbd51..89ee1a5 100644
--- a/pthread_join.c
+++ b/pthread_join.c
@@ -83,22 +83,62 @@ pthread_join (pthread_t thread, void **value_ptr)
int result;
pthread_t self;
- /* This is the proper way to test for a valid thread ID */
- result = pthread_kill (thread, 0);
- if (0 != result)
+ /*
+ * Possibilities for the target thread on entry to pthread_join():
+ *
+ * 1) the target thread is detached, in which case it could be destroyed and
+ * it's thread id (struct) reused before we get the reuse lock;
+ * 2) the target thread is joinable, in which case it will not be destroyed
+ * until it has been joined, and it's thread id cannot be reused before we
+ * get access to it.
+ *
+ * Only (1) is a potential race condition.
+ * While (1) is possibly an application programming error, pthread_join is
+ * required to handle it with an error.
+ * If an application attempts to join a detached thread that exits before we
+ * get the reuse lock, it's thread struct could be reused for a new joinable
+ * thread before we get the reuse_lock and we will then be joining the wrong
+ * thread.
+ *
+ * To fix this properly would require a reuse count as part of the thread id
+ * so that we could confirm that we are working with the same thread. This
+ * option will require a major change to the API, forcing recompilation of
+ * applications that use the library.
+ *
+ * Cheaper alternatives that do not change the API are:
+ * - separate reuse stacks could be used for detached and joinable
+ * threads. Threads which detach themselves will not present a race condition
+ * because they will be placed on a different stack to that which provides
+ * reusable structs to new joinable threads.
+ * The problem with this solution is that, an application that creates
+ * joinable threads which then detach themselves can cause the detach reuse
+ * stack to grow indefinitely.
+ *
+ * - maintain a sufficiently large reuse pool and manage it as a FIFO
+ * queue. This would prevent the immediate reuse of thread structs, but this
+ * only provides a statistical safeguard, not a deterministic one.
+ *
+ * - include an entry point array at the start of each thread struct so that
+ * each reuse (modulo N) returns a different thread id (pointer to the thread
+ * struct). Offset 0 from this pointer will contain a validity field and an
+ * offset value field (subtracted from the pointer to obtain the address of
+ * thread struct). This option is similar to including a reuse counter with
+ * the thread id but maintains the thread id as a simple pointer.
+ *
+ * As at 27/08/2004, none of the above have been implemented.
+ */
+
+ EnterCriticalSection (&ptw32_thread_reuse_lock);
+ /*
+ * The first test is the same as pthread_kill(thread, 0), duplicated here
+ * so that we can use the reuse_lock to ensure the thread isn't destroyed
+ * and reused before we've finished with the POSIX thread struct.
+ */
+ if (NULL == thread
+ || NULL == thread->threadH
+ || THREAD_PRIORITY_ERROR_RETURN == GetThreadPriority (thread->threadH))
{
- return result;
- }
-
- self = pthread_self ();
- if (NULL == self)
- {
- return ENOENT;
- }
-
- if (0 != pthread_equal (self, thread))
- {
- result = EDEADLK;
+ result = ESRCH;
}
else if (PTHREAD_CREATE_DETACHED == thread->detachState)
{
@@ -106,60 +146,81 @@ pthread_join (pthread_t thread, void **value_ptr)
}
else
{
- /*
- * Pthread_join is a cancelation point.
- * If we are canceled then our target thread must not be
- * detached (destroyed). This is guarranteed because
- * pthreadCancelableWait will not return if we
- * are canceled.
+ /*
+ * The target thread is joinable and can't be reused before we join it.
*/
- result = pthreadCancelableWait (thread->threadH);
- if (result == 0)
+ LeaveCriticalSection (&ptw32_thread_reuse_lock);
+
+ if (NULL == (self = pthread_self ()))
+ {
+ result = ENOENT;
+ }
+ else if (0 != pthread_equal (self, thread))
+ {
+ result = EDEADLK;
+ }
+ else
{
+ /*
+ * Pthread_join is a cancelation point.
+ * If we are canceled then our target thread must not be
+ * detached (destroyed). This is guarranteed because
+ * pthreadCancelableWait will not return if we
+ * are canceled.
+ */
+ result = pthreadCancelableWait (thread->threadH);
+
+ if (0 == result)
+ {
#if ! defined (__MINGW32__) || defined (__MSVCRT__) || defined (__DMC__)
- if (value_ptr != NULL
- && !GetExitCodeThread (thread->threadH, (LPDWORD) value_ptr))
- {
- result = ESRCH;
- }
- else
- {
+ if (value_ptr != NULL
+ && !GetExitCodeThread (thread->threadH, (LPDWORD) value_ptr))
+ {
+ result = ESRCH;
+ }
+ else
+ {
+ /*
+ * The result of making multiple simultaneous calls to
+ * pthread_join() specifying the same target is undefined.
+ */
+ ptw32_threadDestroy (thread);
+ }
+
+#else /* __MINGW32__ && ! __MSVCRT__ */
+
+ /*
+ * If using CRTDLL, the thread may have exited, and endthread
+ * will have closed the handle.
+ */
+ if (value_ptr != NULL)
+ {
+ *value_ptr = thread->exitStatus;
+ }
+
/*
* The result of making multiple simultaneous calls to
* pthread_join() specifying the same target is undefined.
*/
ptw32_threadDestroy (thread);
- }
-#else /* __MINGW32__ && ! __MSVCRT__ */
+#endif /* __MINGW32__ && ! __MSVCRT__ */
- /*
- * If using CRTDLL, the thread may have exited, and endthread
- * will have closed the handle.
- */
- if (value_ptr != NULL)
+ }
+ else
{
- *value_ptr = thread->exitStatus;
+ result = ESRCH;
}
-
- /*
- * The result of making multiple simultaneous calls to
- * pthread_join() specifying the same target is undefined.
- */
- ptw32_threadDestroy (thread);
-
-#endif /* __MINGW32__ && ! __MSVCRT__ */
-
- }
- else
- {
- result = ESRCH;
}
+
+ goto FAIL0;
}
+ LeaveCriticalSection (&ptw32_thread_reuse_lock);
+FAIL0:
return (result);
} /* pthread_join */
diff --git a/pthread_once.c b/pthread_once.c
index d37ee3d..34dacea 100644
--- a/pthread_once.c
+++ b/pthread_once.c
@@ -86,6 +86,18 @@ pthread_once (pthread_once_t * once_control, void (*init_routine) (void))
result = 0;
}
+ /*
+ * The race condition involving once_control->done is harmless.
+ * The problem experienced in MPU environments with multibyte variables
+ * is also not a problem because the value (initially zero i.e. PTW32_FALSE)
+ * is only ever tested for non-zero. In the event of a race occuring, the
+ * worst result is that up to N-1 threads (N = number of CPUs) may enter the
+ * while loop and yield their run state unnecessarily, and this can only
+ * ever occur at most once.
+ *
+ * The alternatives are to use a condition variable (overkill?), or
+ * InterlockedCompareExchange() to test/set once_control->done.
+ */
if (!once_control->done)
{
if (InterlockedIncrement (&(once_control->started)) == 0)
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 0a408b8..2d0c570 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -6,6 +6,10 @@
* exit4.c (main): Likewise.
* exit5.c (main): Likewise.
+2004-08-26 Ross Johnson <rpj@callisto.canberra.edu.au>
+
+ * create3.c: New test.
+
2004-06-21 Ross Johnson <rpj@callisto.canberra.edu.au>
* mutex2r.c: New test.
diff --git a/tests/GNUmakefile b/tests/GNUmakefile
index 323f24b..04f9943 100644
--- a/tests/GNUmakefile
+++ b/tests/GNUmakefile
@@ -94,7 +94,7 @@ TESTS = sizes loadfree \
spin1 spin2 spin3 spin4 \
barrier1 barrier2 barrier3 barrier4 barrier5 \
exception1 exception2 exception3 \
- cancel9
+ cancel9 create3
BENCHTESTS = \
benchtest1 benchtest2 benchtest3 benchtest4 benchtest5
@@ -180,6 +180,7 @@ context1.pass: cancel2.pass
count1.pass: join1.pass
create1.pass: mutex2.pass
create2.pass: create1.pass
+create3.pass:
delay1.pass: cancel2.pass
delay2.pass: delay1.pass
equal1.pass: create1.pass
diff --git a/tests/Makefile b/tests/Makefile
index 6aefab7..31979a4 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -110,7 +110,7 @@ PASSES= sizes.pass loadfree.pass \
spin1.pass spin2.pass spin3.pass spin4.pass \
barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \
exception1.pass exception2.pass exception3.pass \
- cancel9
+ cancel9 create3
BENCHRESULTS = \
benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench
@@ -264,6 +264,7 @@ context1.pass: cancel2.pass
count1.pass: join1.pass
create1.pass: mutex2.pass
create2.pass: create1.pass
+create3.pass:
delay1.pass:
delay2.pass: delay1.pass
equal1.pass: create1.pass