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;
}

Reply via email to