| Index: nspr/pr/src/pthreads/ptsynch.c
 | 
| ===================================================================
 | 
| --- nspr/pr/src/pthreads/ptsynch.c	(revision 257452)
 | 
| +++ nspr/pr/src/pthreads/ptsynch.c	(working copy)
 | 
| @@ -169,6 +169,9 @@
 | 
|  
 | 
|  PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
 | 
|  {
 | 
| +    /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or
 | 
| +     * |tid| field of the current thread's PRThread structure because
 | 
| +     * _pt_root calls PR_Lock before setting thred->id and thred->tid. */
 | 
|      PRIntn rv;
 | 
|      PR_ASSERT(lock != NULL);
 | 
|      rv = pthread_mutex_lock(&lock->mutex);
 | 
| @@ -189,14 +192,15 @@
 | 
|  
 | 
|  PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
 | 
|  {
 | 
| +    pthread_t self = pthread_self();
 | 
|      PRIntn rv;
 | 
|  
 | 
|      PR_ASSERT(lock != NULL);
 | 
|      PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
 | 
|      PR_ASSERT(PR_TRUE == lock->locked);
 | 
| -    PR_ASSERT(pthread_equal(lock->owner, pthread_self()));
 | 
| +    PR_ASSERT(pthread_equal(lock->owner, self));
 | 
|  
 | 
| -    if (!lock->locked || !pthread_equal(lock->owner, pthread_self()))
 | 
| +    if (!lock->locked || !pthread_equal(lock->owner, self))
 | 
|          return PR_FAILURE;
 | 
|  
 | 
|      lock->locked = PR_FALSE;
 | 
| @@ -293,7 +297,7 @@
 | 
|                      notified->cv[index].times = -1;
 | 
|                  else if (-1 != notified->cv[index].times)
 | 
|                      notified->cv[index].times += 1;
 | 
| -                goto finished;  /* we're finished */
 | 
| +                return;  /* we're finished */
 | 
|              }
 | 
|          }
 | 
|          /* if not full, enter new CV in this array */
 | 
| @@ -310,10 +314,6 @@
 | 
|      notified->cv[index].times = (broadcast) ? -1 : 1;
 | 
|      notified->cv[index].cv = cvar;
 | 
|      notified->length += 1;
 | 
| -
 | 
| -finished:
 | 
| -    PR_ASSERT(PR_TRUE == cvar->lock->locked);
 | 
| -    PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
 | 
|  }  /* pt_PostNotifyToCvar */
 | 
|  
 | 
|  PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock)
 | 
| @@ -427,54 +427,95 @@
 | 
|  /**************************************************************/
 | 
|  /**************************************************************/
 | 
|  
 | 
| +/*
 | 
| + * Notifies just get posted to the monitor. The actual notification is done
 | 
| + * when the monitor is fully exited so that MP systems don't contend for a
 | 
| + * monitor that they can't enter.
 | 
| + */
 | 
| +static void pt_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast)
 | 
| +{
 | 
| +    PR_ASSERT(NULL != mon);
 | 
| +    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);
 | 
| +
 | 
| +    /* mon->notifyTimes is protected by the monitor, so we don't need to
 | 
| +     * acquire mon->lock.
 | 
| +     */
 | 
| +    if (broadcast)
 | 
| +        mon->notifyTimes = -1;
 | 
| +    else if (-1 != mon->notifyTimes)
 | 
| +        mon->notifyTimes += 1;
 | 
| +}  /* pt_PostNotifyToMonitor */
 | 
| +
 | 
| +static void pt_PostNotifiesFromMonitor(pthread_cond_t *cv, PRIntn times)
 | 
| +{
 | 
| +    PRIntn rv;
 | 
| +
 | 
| +    /*
 | 
| +     * Time to actually notify any waits that were affected while the monitor
 | 
| +     * was entered.
 | 
| +     */
 | 
| +    PR_ASSERT(NULL != cv);
 | 
| +    PR_ASSERT(0 != times);
 | 
| +    if (-1 == times)
 | 
| +    {
 | 
| +        rv = pthread_cond_broadcast(cv);
 | 
| +        PR_ASSERT(0 == rv);
 | 
| +    }
 | 
| +    else
 | 
| +    {
 | 
| +        while (times-- > 0)
 | 
| +        {
 | 
| +            rv = pthread_cond_signal(cv);
 | 
| +            PR_ASSERT(0 == rv);
 | 
| +        }
 | 
| +    }
 | 
| +}  /* pt_PostNotifiesFromMonitor */
 | 
| +
 | 
|  PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void)
 | 
|  {
 | 
|      PRMonitor *mon;
 | 
| -    PRCondVar *cvar;
 | 
|      int rv;
 | 
|  
 | 
|      if (!_pr_initialized) _PR_ImplicitInitialization();
 | 
|  
 | 
| -    cvar = PR_NEWZAP(PRCondVar);
 | 
| -    if (NULL == cvar)
 | 
| -    {
 | 
| -        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
 | 
| -        return NULL;
 | 
| -    }
 | 
|      mon = PR_NEWZAP(PRMonitor);
 | 
|      if (mon == NULL)
 | 
|      {
 | 
| -        PR_Free(cvar);
 | 
|          PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
 | 
|          return NULL;
 | 
|      }
 | 
|  
 | 
| -    rv = _PT_PTHREAD_MUTEX_INIT(mon->lock.mutex, _pt_mattr); 
 | 
| +    rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr);
 | 
|      PR_ASSERT(0 == rv);
 | 
|      if (0 != rv)
 | 
| -    {
 | 
| -        PR_Free(mon);
 | 
| -        PR_Free(cvar);
 | 
| -        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
 | 
| -        return NULL;
 | 
| -    }
 | 
| +        goto error1;
 | 
|  
 | 
|      _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
 | 
|  
 | 
| -    mon->cvar = cvar;
 | 
| -    rv = _PT_PTHREAD_COND_INIT(mon->cvar->cv, _pt_cvar_attr); 
 | 
| +    rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr);
 | 
|      PR_ASSERT(0 == rv);
 | 
| +    if (0 != rv)
 | 
| +        goto error2;
 | 
| +
 | 
| +    rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    if (0 != rv)
 | 
| +        goto error3;
 | 
| +
 | 
| +    mon->notifyTimes = 0;
 | 
|      mon->entryCount = 0;
 | 
| -    mon->cvar->lock = &mon->lock;
 | 
| -    if (0 != rv)
 | 
| -    {
 | 
| -        pthread_mutex_destroy(&mon->lock.mutex);
 | 
| -        PR_Free(mon);
 | 
| -        PR_Free(cvar);
 | 
| -        PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
 | 
| -        return NULL;
 | 
| -    }
 | 
| +    mon->refCount = 1;
 | 
| +    mon->name = NULL;
 | 
|      return mon;
 | 
| +
 | 
| +error3:
 | 
| +    pthread_cond_destroy(&mon->entryCV);
 | 
| +error2:
 | 
| +    pthread_mutex_destroy(&mon->lock);
 | 
| +error1:
 | 
| +    PR_Free(mon);
 | 
| +    PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0);
 | 
| +    return NULL;
 | 
|  }  /* PR_NewMonitor */
 | 
|  
 | 
|  PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name)
 | 
| @@ -488,89 +529,141 @@
 | 
|  PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon)
 | 
|  {
 | 
|      int rv;
 | 
| +
 | 
|      PR_ASSERT(mon != NULL);
 | 
| -    PR_DestroyCondVar(mon->cvar);
 | 
| -    rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv);
 | 
| +    if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0)
 | 
| +    {
 | 
| +        rv = pthread_cond_destroy(&mon->waitCV); PR_ASSERT(0 == rv);
 | 
| +        rv = pthread_cond_destroy(&mon->entryCV); PR_ASSERT(0 == rv);
 | 
| +        rv = pthread_mutex_destroy(&mon->lock); PR_ASSERT(0 == rv);
 | 
|  #if defined(DEBUG)
 | 
|          memset(mon, 0xaf, sizeof(PRMonitor));
 | 
|  #endif
 | 
| -    PR_Free(mon);    
 | 
| +        PR_Free(mon);
 | 
| +    }
 | 
|  }  /* PR_DestroyMonitor */
 | 
|  
 | 
| -
 | 
|  /* The GC uses this; it is quite arguably a bad interface.  I'm just 
 | 
|   * duplicating it for now - XXXMB
 | 
|   */
 | 
|  PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon)
 | 
|  {
 | 
|      pthread_t self = pthread_self();
 | 
| +    PRIntn rv;
 | 
| +    PRIntn count = 0;
 | 
| +
 | 
| +    rv = pthread_mutex_lock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
|      if (pthread_equal(mon->owner, self))
 | 
| -        return mon->entryCount;
 | 
| -    return 0;
 | 
| +        count = mon->entryCount;
 | 
| +    rv = pthread_mutex_unlock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    return count;
 | 
|  }
 | 
|  
 | 
|  PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon)
 | 
|  {
 | 
| -    PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(&mon->lock);
 | 
| +#if defined(DEBUG) || defined(FORCE_PR_ASSERT)
 | 
| +    PRIntn rv;
 | 
| +
 | 
| +    rv = pthread_mutex_lock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    PR_ASSERT(mon->entryCount != 0 &&
 | 
| +              pthread_equal(mon->owner, pthread_self()));
 | 
| +    rv = pthread_mutex_unlock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +#endif
 | 
|  }
 | 
|  
 | 
|  PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon)
 | 
|  {
 | 
|      pthread_t self = pthread_self();
 | 
| +    PRIntn rv;
 | 
|  
 | 
|      PR_ASSERT(mon != NULL);
 | 
| -    /*
 | 
| -     * This is safe only if mon->owner (a pthread_t) can be
 | 
| -     * read in one instruction.  Perhaps mon->owner should be
 | 
| -     * a "PRThread *"?
 | 
| -     */
 | 
| -    if (!pthread_equal(mon->owner, self))
 | 
| +    rv = pthread_mutex_lock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    if (mon->entryCount != 0)
 | 
|      {
 | 
| -        PR_Lock(&mon->lock);
 | 
| -        /* and now I have the lock */
 | 
| -        PR_ASSERT(0 == mon->entryCount);
 | 
| -        PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
 | 
| -        _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);
 | 
| +        if (pthread_equal(mon->owner, self))
 | 
| +            goto done;
 | 
| +        while (mon->entryCount != 0)
 | 
| +        {
 | 
| +            rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
 | 
| +            PR_ASSERT(0 == rv);
 | 
| +        }
 | 
|      }
 | 
| +    /* and now I have the monitor */
 | 
| +    PR_ASSERT(0 == mon->notifyTimes);
 | 
| +    PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
 | 
| +    _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);
 | 
| +
 | 
| +done:
 | 
|      mon->entryCount += 1;
 | 
| +    rv = pthread_mutex_unlock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
|  }  /* PR_EnterMonitor */
 | 
|  
 | 
|  PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon)
 | 
|  {
 | 
|      pthread_t self = pthread_self();
 | 
| +    PRIntn rv;
 | 
| +    PRBool notifyEntryWaiter = PR_FALSE;
 | 
| +    PRIntn notifyTimes = 0;
 | 
|  
 | 
|      PR_ASSERT(mon != NULL);
 | 
| -    /* The lock better be that - locked */
 | 
| -    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
 | 
| -    /* we'd better be the owner */
 | 
| +    rv = pthread_mutex_lock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    /* the entries should be > 0 and we'd better be the owner */
 | 
| +    PR_ASSERT(mon->entryCount > 0);
 | 
|      PR_ASSERT(pthread_equal(mon->owner, self));
 | 
| -    if (!pthread_equal(mon->owner, self))
 | 
| +    if (mon->entryCount == 0 || !pthread_equal(mon->owner, self))
 | 
| +    {
 | 
| +        rv = pthread_mutex_unlock(&mon->lock);
 | 
| +        PR_ASSERT(0 == rv);
 | 
|          return PR_FAILURE;
 | 
| +    }
 | 
|  
 | 
| -    /* if it's locked and we have it, then the entries should be > 0 */
 | 
| -    PR_ASSERT(mon->entryCount > 0);
 | 
|      mon->entryCount -= 1;  /* reduce by one */
 | 
|      if (mon->entryCount == 0)
 | 
|      {
 | 
| -        /* and if it transitioned to zero - unlock */
 | 
| -        _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);  /* make the owner unknown */
 | 
| -        PR_Unlock(&mon->lock);
 | 
| +        /* and if it transitioned to zero - notify an entry waiter */
 | 
| +        /* make the owner unknown */
 | 
| +        _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
 | 
| +        notifyEntryWaiter = PR_TRUE;
 | 
| +        notifyTimes = mon->notifyTimes;
 | 
| +        mon->notifyTimes = 0;
 | 
| +        /* We will access the members of 'mon' after unlocking mon->lock.
 | 
| +         * Add a reference. */
 | 
| +        PR_ATOMIC_INCREMENT(&mon->refCount);
 | 
|      }
 | 
| +    rv = pthread_mutex_unlock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    if (notifyEntryWaiter)
 | 
| +    {
 | 
| +        if (notifyTimes)
 | 
| +            pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes);
 | 
| +        rv = pthread_cond_signal(&mon->entryCV);
 | 
| +        PR_ASSERT(0 == rv);
 | 
| +        /* We are done accessing the members of 'mon'. Release the
 | 
| +         * reference. */
 | 
| +        PR_DestroyMonitor(mon);
 | 
| +    }
 | 
|      return PR_SUCCESS;
 | 
|  }  /* PR_ExitMonitor */
 | 
|  
 | 
|  PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout)
 | 
|  {
 | 
|      PRStatus rv;
 | 
| -    PRInt16 saved_entries;
 | 
| +    PRUint32 saved_entries;
 | 
|      pthread_t saved_owner;
 | 
|  
 | 
|      PR_ASSERT(mon != NULL);
 | 
| -    /* we'd better be locked */
 | 
| -    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
 | 
| -    /* and the entries better be positive */
 | 
| +    rv = pthread_mutex_lock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +    /* the entries better be positive */
 | 
|      PR_ASSERT(mon->entryCount > 0);
 | 
| -    /* and it better be by us */
 | 
| +    /* and it better be owned by us */
 | 
|      PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
 | 
|  
 | 
|      /* tuck these away 'till later */
 | 
| @@ -578,43 +671,52 @@
 | 
|      mon->entryCount = 0;
 | 
|      _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
 | 
|      _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
 | 
| -    
 | 
| -    rv = PR_WaitCondVar(mon->cvar, timeout);
 | 
| +    /*
 | 
| +     * If we have pending notifies, post them now.
 | 
| +     *
 | 
| +     * This is not optimal. We're going to post these notifies
 | 
| +     * while we're holding the lock. That means on MP systems
 | 
| +     * that they are going to collide for the lock that we will
 | 
| +     * hold until we actually wait.
 | 
| +     */
 | 
| +    if (0 != mon->notifyTimes)
 | 
| +    {
 | 
| +        pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
 | 
| +        mon->notifyTimes = 0;
 | 
| +    }
 | 
| +    rv = pthread_cond_signal(&mon->entryCV);
 | 
| +    PR_ASSERT(0 == rv);
 | 
|  
 | 
| -    /* reinstate the intresting information */
 | 
| +    if (timeout == PR_INTERVAL_NO_TIMEOUT)
 | 
| +        rv = pthread_cond_wait(&mon->waitCV, &mon->lock);
 | 
| +    else
 | 
| +        rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout);
 | 
| +    PR_ASSERT(0 == rv);
 | 
| +
 | 
| +    while (mon->entryCount != 0)
 | 
| +    {
 | 
| +        rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
 | 
| +        PR_ASSERT(0 == rv);
 | 
| +    }
 | 
| +    PR_ASSERT(0 == mon->notifyTimes);
 | 
| +    /* reinstate the interesting information */
 | 
|      mon->entryCount = saved_entries;
 | 
|      _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);
 | 
|  
 | 
| +    rv = pthread_mutex_unlock(&mon->lock);
 | 
| +    PR_ASSERT(0 == rv);
 | 
|      return rv;
 | 
|  }  /* PR_Wait */
 | 
|  
 | 
|  PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon)
 | 
|  {
 | 
| -    PR_ASSERT(NULL != mon);
 | 
| -    /* we'd better be locked */
 | 
| -    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
 | 
| -    /* and the entries better be positive */
 | 
| -    PR_ASSERT(mon->entryCount > 0);
 | 
| -    /* and it better be by us */
 | 
| -    PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
 | 
| -
 | 
| -    pt_PostNotifyToCvar(mon->cvar, PR_FALSE);
 | 
| -
 | 
| +    pt_PostNotifyToMonitor(mon, PR_FALSE);
 | 
|      return PR_SUCCESS;
 | 
|  }  /* PR_Notify */
 | 
|  
 | 
|  PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon)
 | 
|  {
 | 
| -    PR_ASSERT(mon != NULL);
 | 
| -    /* we'd better be locked */
 | 
| -    PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex));
 | 
| -    /* and the entries better be positive */
 | 
| -    PR_ASSERT(mon->entryCount > 0);
 | 
| -    /* and it better be by us */
 | 
| -    PR_ASSERT(pthread_equal(mon->owner, pthread_self()));
 | 
| -
 | 
| -    pt_PostNotifyToCvar(mon->cvar, PR_TRUE);
 | 
| -
 | 
| +    pt_PostNotifyToMonitor(mon, PR_TRUE);
 | 
|      return PR_SUCCESS;
 | 
|  }  /* PR_NotifyAll */
 | 
|  
 | 
| 
 |