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 */ |