This is a slightly modified version of my still pending patch. The
modifications were made to support rwlocks.
2003-02-27 Thomas Pfaff <[EMAIL PROTECTED]>
* thread.h (pthread_cond::ExitingWait): Remove.
(pthread_cond::mutex): Ditto.
(pthread_cond::cond_access): Ditto.
(pthread_cond::win32_obj_id): Ditto.
(pthread_cond::TimedWait): Ditto.
(pthread_cond::BroadCast): Ditto.
(pthread_cond::Signal): Ditto.
(pthread_cond::waiting): Change type to unsigned long.
(pthread_cond::pending): New member.
(pthread_cond::semWait): Ditto.
(pthread_cond::mtxIn): Ditto.
(pthread_cond::mtxOut): Ditto.
(pthread_cond::mtxCond): Ditto.
(pthread_cond::UnBlock): New method.
(pthread_cond::Wait): Ditto.
* thread.cc: Update list of cancellation points.
(pthread_cond::pthread_cond): Rewrite.
(pthread_cond::~pthread_cond): Ditto.
(pthread_cond::TimedWait): Remove.
(pthread_cond::BroadCast): Ditto.
(pthread_cond::Signal): Ditto.
(pthread_cond::UnBlock): Implement.
(pthread_cond::Wait): Ditto.
(pthread_cond::fixup_after_fork): Rewrite.
(pthread_mutex::fixup_after_fork): Remove DETECT_BAD_APP
conditional.
(__pthread_cond_broadcast): Just return 0 if the condition is
not initialized. Call pthread_cond::UnBlock to release blocked
threads.
(__pthread_cond_signal): Ditto.
(__pthread_cond__dowait): Rewrite.
(pthread_cond_timedwait): Add pthread_testcancel call. Fix
waitlength calculation.
(pthread_cond_wait): Add pthread_testcancel call.
diff -urp src.old/winsup/cygwin/thread.cc src/winsup/cygwin/thread.cc
--- src.old/winsup/cygwin/thread.cc 2003-02-27 09:25:49.000000000 +0100
+++ src/winsup/cygwin/thread.cc 2003-02-27 09:28:44.000000000 +0100
@@ -462,8 +462,8 @@ open ()
*pause ()
poll ()
pread ()
-pthread_cond_timedwait ()
-pthread_cond_wait ()
+*pthread_cond_timedwait ()
+*pthread_cond_wait ()
*pthread_join ()
*pthread_testcancel ()
putmsg ()
@@ -812,36 +812,57 @@ pthread_cond::initMutex ()
api_fatal ("Could not create win32 Mutex for pthread cond static initializer
support.");
}
-pthread_cond::pthread_cond (pthread_condattr *attr):verifyable_object
(PTHREAD_COND_MAGIC)
+pthread_cond::pthread_cond (pthread_condattr *attr) :
+ verifyable_object (PTHREAD_COND_MAGIC),
+ shared (0), waiting (0), pending (0), semWait (NULL),
+ mtxCond(NULL), next (NULL)
{
- int temperr;
- this->shared = attr ? attr->shared : PTHREAD_PROCESS_PRIVATE;
- this->mutex = NULL;
- this->waiting = 0;
-
- this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, /* auto signal reset -
which I think is pthreads like ? */
- false, /* start non signaled */
- NULL /* no name */);
- /* TODO: make a shared mem mutex if out attributes request shared mem cond */
- cond_access = NULL;
- if ((temperr = pthread_mutex_init (&this->cond_access, NULL)))
+ pthread_mutex *verifyable_mutex_obj;
+
+ if (attr)
+ if (attr->shared != PTHREAD_PROCESS_PRIVATE)
+ {
+ magic = 0;
+ return;
+ }
+
+ verifyable_mutex_obj = &mtxIn;
+ if (!pthread_mutex::isGoodObject (&verifyable_mutex_obj))
{
- system_printf ("couldn't init mutex, this %p errno %d", this, temperr);
- /* we need the mutex for correct behaviour */
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtxIn.type = PTHREAD_MUTEX_NORMAL;
+
+ verifyable_mutex_obj = &mtxOut;
+ if (!pthread_mutex::isGoodObject (&verifyable_mutex_obj))
+ {
+ thread_printf ("Internal cond mutex is not valid. this %p", this);
+ magic = 0;
+ return;
+ }
+ /* Change the mutex type to NORMAL to speed up mutex operations */
+ mtxOut.type = PTHREAD_MUTEX_NORMAL;
+
+ semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!semWait)
+ {
+ debug_printf ("CreateSemaphore failed. %E");
+ magic = 0;
+ return;
}
- if (!this->win32_obj_id)
- magic = 0;
/* threadsafe addition is easy */
next = (pthread_cond *) InterlockedExchangePointer (&MT_INTERFACE->conds, this);
}
pthread_cond::~pthread_cond ()
{
- if (win32_obj_id)
- CloseHandle (win32_obj_id);
- pthread_mutex_destroy (&cond_access);
+ if (semWait)
+ CloseHandle (semWait);
+
/* I'm not 100% sure the next bit is threadsafe. I think it is... */
if (MT_INTERFACE->conds == this)
InterlockedExchangePointer (&MT_INTERFACE->conds, this->next);
@@ -856,132 +877,125 @@ pthread_cond::~pthread_cond ()
}
void
-pthread_cond::BroadCast ()
+pthread_cond::UnBlock (const bool all)
{
- /* TODO: implement the same race fix as Signal has */
- if (pthread_mutex_lock (&cond_access))
- system_printf ("Failed to lock condition variable access mutex, this %p", this);
- int count = waiting;
- if (!pthread_mutex::isGoodObject (&mutex))
- {
- if (pthread_mutex_unlock (&cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
this);
- /* This isn't and API error - users are allowed to call this when no threads
- are waiting
- system_printf ("Broadcast called with invalid mutex");
- */
- return;
- }
- while (count--)
- PulseEvent (win32_obj_id);
- if (pthread_mutex_unlock (&cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p", this);
-}
+ unsigned long releaseable;
-void
-pthread_cond::Signal ()
-{
- if (pthread_mutex_lock (&cond_access))
- system_printf ("Failed to lock condition variable access mutex, this %p", this);
- if (!pthread_mutex::isGoodObject (&mutex))
- {
- if (pthread_mutex_unlock (&cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
- this);
- return;
- }
- int temp = waiting;
- if (!temp)
- /* nothing to signal */
- {
- if (pthread_mutex_unlock (&cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
this);
- return;
- }
- /* Prime the detection flag */
- ExitingWait = 1;
- /* Signal any waiting thread */
- PulseEvent (win32_obj_id);
- /* No one can start waiting until we release the condition access mutex */
- /* The released thread will decrement waiting when it gets a time slice...
- without waiting for the access mutex
- * InterLockedIncrement on 98 +, NT4 + returns the incremented value.
- * On 95, nt 3.51 < it returns a sign correct number - 0=0, + for greater than 0, -
- * for less than 0.
- * Because of this we cannot spin on the waiting count, but rather we need a
- * dedicated flag for a thread exiting the Wait function.
- * Also not that Interlocked* sync CPU caches with memory.
- */
- int spins = 10;
- /* When ExitingWait is nonzero after a decrement, the leaving thread has
- * done it's thing
+ /*
+ * Block outgoing threads (and avoid simultanous unblocks)
*/
- while (InterlockedDecrement (&ExitingWait) == 0 && spins)
+ mtxOut.Lock ();
+
+ releaseable = waiting - pending;
+ if (releaseable)
{
- InterlockedIncrement (&ExitingWait);
- /* give up the cpu to force a context switch. */
- low_priority_sleep (0);
- if (spins == 5)
- /* we've had 5 timeslices, and the woken thread still hasn't done it's
- * thing - maybe we raced it with the event? */
- PulseEvent (win32_obj_id);
- spins--;
+ unsigned long released;
+
+ if (!pending)
+ {
+ /*
+ * Block incoming threads until all waiting threads are released.
+ */
+ mtxIn.Lock ();
+
+ /*
+ * Calculate releaseable again because threads can enter until
+ * the semaphore has been taken, but they can not leave, therefore pending
+ * is unchanged and releaseable can only get higher
+ */
+ releaseable = waiting - pending;
+ }
+
+ released = all ? releaseable : 1;
+ pending += released;
+ /*
+ * Signal threads
+ */
+ ::ReleaseSemaphore (semWait, released, NULL);
}
- if (waiting + 1 != temp)
- system_printf ("Released too many threads - %d now %d originally", waiting, temp);
- if (pthread_mutex_unlock (&cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p", this);
+
+ /*
+ * And let the threads release.
+ */
+ mtxOut.UnLock ();
}
int
-pthread_cond::TimedWait (DWORD dwMilliseconds)
+pthread_cond::Wait (pthread_mutex_t mutex, DWORD dwMilliseconds)
{
DWORD rv;
- // FIXME: race condition (potentially drop events
- // Possible solution (single process only) - place this in a critical section.
- mutex->UnLock ();
- rv = WaitForSingleObject (win32_obj_id, dwMilliseconds);
-#if 0
- /* we need to use native win32 mutex's here, because the cygwin ones now use
- * critical sections, which are faster, but introduce a race _here_. Until then
- * The NT variant of the code is redundant.
- */
-
- rv = SignalObjectAndWait (mutex->win32_obj_id, win32_obj_id, dwMilliseconds,
- false);
-#endif
- switch (rv)
+ mtxIn.Lock ();
+ if (1 == InterlockedIncrement ((long *)&waiting))
+ mtxCond = mutex;
+ else if (mtxCond != mutex)
{
- case WAIT_FAILED:
- return 0; /* POSIX doesn't allow errors after we modify
the mutex state */
- case WAIT_ABANDONED:
- case WAIT_TIMEOUT:
- return ETIMEDOUT;
- case WAIT_OBJECT_0:
- return 0; /* we have been signaled */
- default:
- return 0;
+ InterlockedDecrement ((long *)&waiting);
+ mtxIn.UnLock ();
+ return EINVAL;
}
+ mtxIn.UnLock ();
+
+ /*
+ * Release the mutex and wait on semaphore
+ */
+ ++mutex->condwaits;
+ mutex->UnLock ();
+
+ rv = pthread::cancelable_wait (semWait, dwMilliseconds, false);
+
+ mtxOut.Lock ();
+
+ if (rv != WAIT_OBJECT_0)
+ {
+ /*
+ * It might happen that a signal is sent while the thread got canceled
+ * or timed out. Try to take one.
+ * If the thread gets one than a signal|broadcast is in progress.
+ */
+ if (WAIT_OBJECT_0 == WaitForSingleObject (semWait, 0))
+ /*
+ * thread got cancelled ot timed out while a signalling is in progress.
+ * Set wait result back to signaled
+ */
+ rv = WAIT_OBJECT_0;
+ }
+
+ InterlockedDecrement ((long *)&waiting);
+
+ if (rv == WAIT_OBJECT_0 && 0 == --pending)
+ /*
+ * All signaled threads are released,
+ * new threads can enter Wait
+ */
+ mtxIn.UnLock ();
+
+ mtxOut.UnLock ();
+
+ mutex->Lock ();
+ --mutex->condwaits;
+
+ if (rv == WAIT_CANCELED)
+ pthread::static_cancel_self ();
+ else if (rv == WAIT_TIMEOUT)
+ return ETIMEDOUT;
+
+ return 0;
}
void
pthread_cond::fixup_after_fork ()
{
- debug_printf ("cond %x in fixup_after_fork", this);
- if (shared != PTHREAD_PROCESS_PRIVATE)
- api_fatal ("doesn't understand PROCESS_SHARED condition variables");
- /* FIXME: duplicate code here and in the constructor. */
- this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, false, NULL);
- if (!win32_obj_id)
- api_fatal ("failed to create new win32 mutex");
-#if DETECT_BAD_APPS
- if (waiting)
- api_fatal ("Forked () while a condition variable has waiting threads.\nReport to
[EMAIL PROTECTED]");
-#else
- waiting = 0;
- mutex = NULL;
-#endif
+ waiting = pending = 0;
+ mtxCond = NULL;
+
+ /* Unlock eventually locked mutexes */
+ mtxIn.UnLock ();
+ mtxOut.UnLock ();
+
+ semWait = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL);
+ if (!semWait)
+ api_fatal ("pthread_cond::fixup_after_fork () failed to recreate win32
semaphore");
}
/* pthread_key */
@@ -1325,12 +1339,7 @@ pthread_mutex::fixup_after_fork ()
if (!win32_obj_id)
api_fatal ("pthread_mutex::fixup_after_fork () failed to recreate win32 semaphore
for mutex");
-#if DETECT_BAD_APPS
- if (condwaits)
- api_fatal ("Forked () while a mutex has condition variables waiting on
it.\nReport to [EMAIL PROTECTED]");
-#else
condwaits = 0;
-#endif
}
bool
@@ -2168,11 +2177,11 @@ int
__pthread_cond_broadcast (pthread_cond_t *cond)
{
if (pthread_cond::isGoodInitializer (cond))
- pthread_cond::init (cond, NULL);
+ return 0;
if (!pthread_cond::isGoodObject (cond))
return EINVAL;
- (*cond)->BroadCast ();
+ (*cond)->UnBlock (true);
return 0;
}
@@ -2181,82 +2190,47 @@ int
__pthread_cond_signal (pthread_cond_t *cond)
{
if (pthread_cond::isGoodInitializer (cond))
- pthread_cond::init (cond, NULL);
+ return 0;
if (!pthread_cond::isGoodObject (cond))
return EINVAL;
- (*cond)->Signal ();
+ (*cond)->UnBlock (false);
return 0;
}
-int
+static int
__pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex,
- long waitlength)
+ DWORD waitlength)
{
-// and yes cond_access here is still open to a race. (we increment, context swap,
-// broadcast occurs - we miss the broadcast. the functions aren't split properly.
- int rv;
- pthread_mutex **themutex = NULL;
- if (pthread_mutex::isGoodInitializer (mutex))
- pthread_mutex::init (mutex, NULL);
- themutex = mutex;
+ if (!pthread_mutex::isGoodObject (mutex))
+ return EINVAL;
+ if (!pthread_mutex::canBeUnlocked (mutex))
+ return EPERM;
+
if (pthread_cond::isGoodInitializer (cond))
pthread_cond::init (cond, NULL);
-
- if (!pthread_mutex::isGoodObject (themutex))
- return EINVAL;
if (!pthread_cond::isGoodObject (cond))
return EINVAL;
- /* if the cond variable is blocked, then the above timer test maybe wrong. *shrug**/
- if (pthread_mutex_lock (&(*cond)->cond_access))
- system_printf ("Failed to lock condition variable access mutex, this %p", *cond);
-
- if ((*cond)->waiting)
- if ((*cond)->mutex && ((*cond)->mutex != (*themutex)))
- {
- if (pthread_mutex_unlock (&(*cond)->cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
*cond);
- return EINVAL;
- }
- InterlockedIncrement (&((*cond)->waiting));
-
- (*cond)->mutex = (*themutex);
- InterlockedIncrement (&((*themutex)->condwaits));
- if (pthread_mutex_unlock (&(*cond)->cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
*cond);
- /* At this point calls to Signal will progress evebn if we aren' yet waiting
- However, the loop there should allow us to get scheduled and call wait,
- and have them call PulseEvent again if we dont' respond. */
- rv = (*cond)->TimedWait (waitlength);
- /* this may allow a race on the mutex acquisition and waits.
- But doing this within the cond access mutex creates a different race */
- InterlockedDecrement (&((*cond)->waiting));
- /* Tell Signal that we have been released */
- InterlockedDecrement (&((*cond)->ExitingWait));
- (*themutex)->Lock ();
- if (pthread_mutex_lock (&(*cond)->cond_access))
- system_printf ("Failed to lock condition variable access mutex, this %p", *cond);
- if ((*cond)->waiting == 0)
- (*cond)->mutex = NULL;
- InterlockedDecrement (&((*themutex)->condwaits));
- if (pthread_mutex_unlock (&(*cond)->cond_access))
- system_printf ("Failed to unlock condition variable access mutex, this %p",
*cond);
-
- return rv;
+ return (*cond)->Wait (*mutex, waitlength);
}
extern "C" int
pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{
+ struct timeval tv;
+ long waitlength;
+
+ pthread_testcancel ();
+
if (check_valid_pointer (abstime))
return EINVAL;
- struct timeb currSysTime;
- long waitlength;
- ftime (&currSysTime);
- waitlength = (abstime->tv_sec - currSysTime.time) * 1000;
+
+ gettimeofday (&tv, NULL);
+ waitlength = abstime->tv_sec * 1000 + abstime->tv_nsec / (1000 * 1000);
+ waitlength -= tv.tv_sec * 1000 + tv.tv_usec / 1000;
if (waitlength < 0)
return ETIMEDOUT;
return __pthread_cond_dowait (cond, mutex, waitlength);
@@ -2265,6 +2239,8 @@ pthread_cond_timedwait (pthread_cond_t *
extern "C" int
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
{
+ pthread_testcancel ();
+
return __pthread_cond_dowait (cond, mutex, INFINITE);
}
diff -urp src.old/winsup/cygwin/thread.h src/winsup/cygwin/thread.h
--- src.old/winsup/cygwin/thread.h 2003-02-27 09:25:49.000000000 +0100
+++ src/winsup/cygwin/thread.h 2003-02-27 09:27:36.000000000 +0100
@@ -494,16 +494,20 @@ public:
static int init (pthread_cond_t *, const pthread_condattr_t *);
int shared;
- LONG waiting;
- LONG ExitingWait;
- pthread_mutex *mutex;
- /* to allow atomic behaviour for cond_broadcast */
- pthread_mutex_t cond_access;
- HANDLE win32_obj_id;
+
+ unsigned long waiting;
+ unsigned long pending;
+ HANDLE semWait;
+
+ pthread_mutex mtxIn;
+ pthread_mutex mtxOut;
+
+ pthread_mutex_t mtxCond;
+
class pthread_cond * next;
- int TimedWait (DWORD dwMilliseconds);
- void BroadCast ();
- void Signal ();
+
+ void UnBlock (const bool all);
+ int Wait (pthread_mutex_t mutex, DWORD dwMilliseconds = INFINITE);
void fixup_after_fork ();
pthread_cond (pthread_condattr *);
/*
* File: condvar9.c
*
*
* Test Synopsis:
* - Test multiple pthread_cond_broadcasts with thread cancelation.
*
* Test Method (Validation or Falsification):
* - Validation
*
* Requirements Tested:
* -
*
* Features Tested:
* -
*
* Cases Tested:
* -
*
* Description:
* - Make NUMTHREADS threads wait on CV, cancel one, broadcast signal them,
* and then repeat.
*
* Environment:
* -
*
* Input:
* - None.
*
* Output:
* - File name, Line number, and failed expression on failure.
* - No output on success.
*
* Assumptions:
* -
*
* Pass Criteria:
* - Process returns zero exit status.
*
* Fail Criteria:
* - Process returns non-zero exit status.
*/
#include "test.h"
#include <sys/timeb.h>
/*
* Create NUMTHREADS threads in addition to the Main thread.
*/
enum {
NUMTHREADS = 9
};
typedef struct bag_t_ bag_t;
struct bag_t_ {
int threadnum;
int started;
/* Add more per-thread state variables here */
};
static bag_t threadbag[NUMTHREADS + 1];
typedef struct cvthing_t_ cvthing_t;
struct cvthing_t_ {
pthread_cond_t notbusy;
pthread_mutex_t lock;
int shared;
};
static cvthing_t cvthing = {
PTHREAD_COND_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
0
};
static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;
static struct timespec abstime = { 0, 0 };
static int awoken;
static void *
mythread(void * arg)
{
bag_t * bag = (bag_t *) arg;
assert(bag == &threadbag[bag->threadnum]);
assert(bag->started == 0);
bag->started = 1;
/* Wait for the start gun */
assert(pthread_mutex_lock(&start_flag) == 0);
assert(pthread_mutex_unlock(&start_flag) == 0);
assert(pthread_mutex_lock(&cvthing.lock) == 0);
/*
* pthread_cond_timedwait is a cancelation point and we
* going to cancel one deliberately.
*/
#ifdef _MSC_VER
#pragma inline_depth(0)
#endif
pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);
while (! (cvthing.shared > 0))
assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
pthread_cleanup_pop(0);
#ifdef _MSC_VER
#pragma inline_depth()
#endif
assert(cvthing.shared > 0);
awoken++;
assert(pthread_mutex_unlock(&cvthing.lock) == 0);
return (void *) 0;
}
int
main()
{
int failed = 0;
int i;
int first, last;
int canceledThreads = 0;
pthread_t t[NUMTHREADS + 1];
struct timeb currSysTime;
const DWORD NANOSEC_PER_MILLISEC = 1000000;
assert((t[0] = pthread_self()) != NULL);
assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);
assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);
/* get current system time */
ftime(&currSysTime);
abstime.tv_sec = currSysTime.time;
abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
abstime.tv_sec += 5;
assert((t[0] = pthread_self()) != NULL);
awoken = 0;
for (first = 1, last = NUMTHREADS / 2;
first < NUMTHREADS;
first = last + 1, last = NUMTHREADS)
{
assert(pthread_mutex_lock(&start_flag) == 0);
for (i = first; i <= last; i++)
{
threadbag[i].started = 0;
threadbag[i].threadnum = i;
assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
assert(pthread_detach(t[i]) == 0);
}
/*
* Code to control or munipulate child threads should probably go here.
*/
cvthing.shared = 0;
assert(pthread_mutex_unlock(&start_flag) == 0);
/*
* Give threads time to start.
*/
Sleep(1000);
assert(pthread_mutex_lock(&cvthing.lock) == 0);
cvthing.shared++;
assert(pthread_mutex_unlock(&cvthing.lock) == 0);
assert(pthread_cancel(t[(first + last) / 2]) == 0);
canceledThreads++;
assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);
/*
* Give threads time to complete.
*/
Sleep(1000);
}
/*
* Standard check that all threads started.
*/
for (i = 1; i <= NUMTHREADS; i++)
{
failed = !threadbag[i].started;
if (failed)
{
fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
}
}
/*
* Cleanup the CV.
*/
assert(pthread_mutex_destroy(&cvthing.lock) == 0);
assert(cvthing.lock == NULL);
assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
assert(cvthing.notbusy == NULL);
assert(!failed);
/*
* Check any results here.
*/
assert(awoken == NUMTHREADS - canceledThreads);
/*
* Success.
*/
return 0;
}
/*
* File: condvar7.c
*
*
* Test Synopsis:
* - Test pthread_cond_broadcast with thread cancelation.
*
* Test Method (Validation or Falsification):
* - Validation
*
* Requirements Tested:
* -
*
* Features Tested:
* -
*
* Cases Tested:
* -
*
* Description:
* - Test broadcast with NUMTHREADS (=5) waiting CVs, one is canceled while waiting.
*
* Environment:
* -
*
* Input:
* - None.
*
* Output:
* - File name, Line number, and failed expression on failure.
* - No output on success.
*
* Assumptions:
* -
*
* Pass Criteria:
* - Process returns zero exit status.
*
* Fail Criteria:
* - Process returns non-zero exit status.
*/
#include "test.h"
#include <sys/timeb.h>
/*
* Create NUMTHREADS threads in addition to the Main thread.
*/
enum {
NUMTHREADS = 5
};
typedef struct bag_t_ bag_t;
struct bag_t_ {
int threadnum;
int started;
/* Add more per-thread state variables here */
};
static bag_t threadbag[NUMTHREADS + 1];
typedef struct cvthing_t_ cvthing_t;
struct cvthing_t_ {
pthread_cond_t notbusy;
pthread_mutex_t lock;
int shared;
};
static cvthing_t cvthing = {
PTHREAD_COND_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
0
};
static pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;
static struct timespec abstime = { 0, 0 };
static int awoken;
void *
mythread(void * arg)
{
bag_t * bag = (bag_t *) arg;
assert(bag == &threadbag[bag->threadnum]);
assert(bag->started == 0);
bag->started = 1;
/* Wait for the start gun */
assert(pthread_mutex_lock(&start_flag) == 0);
assert(pthread_mutex_unlock(&start_flag) == 0);
assert(pthread_mutex_lock(&cvthing.lock) == 0);
#ifdef _MSC_VER
#pragma inline_depth(0)
#endif
pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);
while (! (cvthing.shared > 0))
assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);
pthread_cleanup_pop(0);
#ifdef _MSC_VER
#pragma inline_depth()
#endif
assert(cvthing.shared > 0);
awoken++;
assert(pthread_mutex_unlock(&cvthing.lock) == 0);
return (void *) 0;
}
int
main()
{
int failed = 0;
int i;
pthread_t t[NUMTHREADS + 1];
struct timeb currSysTime;
const DWORD NANOSEC_PER_MILLISEC = 1000000;
cvthing.shared = 0;
assert((t[0] = pthread_self()) != NULL);
assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);
assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);
assert(pthread_mutex_lock(&start_flag) == 0);
/* get current system time */
ftime(&currSysTime);
abstime.tv_sec = currSysTime.time;
abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;
abstime.tv_sec += 10;
assert((t[0] = pthread_self()) != NULL);
awoken = 0;
for (i = 1; i <= NUMTHREADS; i++)
{
threadbag[i].started = 0;
threadbag[i].threadnum = i;
assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
}
/*
* Code to control or munipulate child threads should probably go here.
*/
assert(pthread_mutex_unlock(&start_flag) == 0);
/*
* Give threads time to start.
*/
Sleep(1000);
assert(pthread_mutex_lock(&cvthing.lock) == 0);
cvthing.shared++;
assert(pthread_mutex_unlock(&cvthing.lock) == 0);
/*
* Cancel one of the threads.
*/
assert(pthread_cancel(t[3]) == 0);
Sleep(500);
/*
* Signal all remaining waiting threads.
*/
assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);
/*
* Give threads time to complete.
*/
Sleep(2000);
/*
* Cleanup the CV.
*/
assert(pthread_mutex_destroy(&cvthing.lock) == 0);
assert(cvthing.lock == NULL);
assert(pthread_cond_destroy(&cvthing.notbusy) == 0);
assert(cvthing.notbusy == NULL);
/*
* Standard check that all threads started.
*/
for (i = 1; i <= NUMTHREADS; i++)
{
failed = !threadbag[i].started;
if (failed)
{
fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
}
}
assert(!failed);
/*
* Check any results here.
*/
assert(awoken == (NUMTHREADS - 1));
/*
* Success.
*/
return 0;
}