* 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.
This commit is contained in:
		@@ -1,3 +1,40 @@
 | 
			
		||||
2003-03-18  Thomas Pfaff  <tpfaff@gmx.net>
 | 
			
		||||
 | 
			
		||||
	* 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.
 | 
			
		||||
 | 
			
		||||
2003-03-18  Thomas Pfaff  <tpfaff@gmx.net>
 | 
			
		||||
 | 
			
		||||
	* include/pthread.h (PTHREAD_MUTEX_NORMAL): New define.
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  pthread_mutex *verifyable_mutex_obj;
 | 
			
		||||
 | 
			
		||||
  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)))
 | 
			
		||||
  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.
 | 
			
		||||
  /* 
 | 
			
		||||
   * Block outgoing threads (and avoid simultanous unblocks)
 | 
			
		||||
   */
 | 
			
		||||
  int spins = 10;
 | 
			
		||||
  /* When ExitingWait is nonzero after a decrement, the leaving thread has
 | 
			
		||||
   * done it's thing
 | 
			
		||||
   */
 | 
			
		||||
  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 cygwin@cygwin.com");
 | 
			
		||||
#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 cygwin@cygwin.com");
 | 
			
		||||
#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 *cond, pthread_mutex_t *mutex,
 | 
			
		||||
extern "C" int
 | 
			
		||||
pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
 | 
			
		||||
{
 | 
			
		||||
  pthread_testcancel ();
 | 
			
		||||
 | 
			
		||||
  return __pthread_cond_dowait (cond, mutex, INFINITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 *);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user