Index: nspr/pr/src/threads/prmon.c |
=================================================================== |
--- nspr/pr/src/threads/prmon.c (revision 257452) |
+++ nspr/pr/src/threads/prmon.c (working copy) |
@@ -8,32 +8,90 @@ |
/************************************************************************/ |
/* |
+ * 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 _PR_PostNotifyToMonitor(PRMonitor *mon, PRBool broadcast) |
+{ |
+ PR_ASSERT(mon != NULL); |
+ 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 (mon->notifyTimes != -1) |
+ mon->notifyTimes += 1; |
+} |
+ |
+static void _PR_PostNotifiesFromMonitor(PRCondVar *cv, PRIntn times) |
+{ |
+ PRStatus rv; |
+ |
+ /* |
+ * Time to actually notify any waits that were affected while the monitor |
+ * was entered. |
+ */ |
+ PR_ASSERT(cv != NULL); |
+ PR_ASSERT(times != 0); |
+ if (times == -1) { |
+ rv = PR_NotifyAllCondVar(cv); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ } else { |
+ while (times-- > 0) { |
+ rv = PR_NotifyCondVar(cv); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ } |
+ } |
+} |
+ |
+/* |
** Create a new monitor. |
*/ |
PR_IMPLEMENT(PRMonitor*) PR_NewMonitor() |
{ |
PRMonitor *mon; |
- PRCondVar *cvar; |
- PRLock *lock; |
+ PRStatus rv; |
+ if (!_pr_initialized) _PR_ImplicitInitialization(); |
+ |
mon = PR_NEWZAP(PRMonitor); |
- if (mon) { |
- lock = PR_NewLock(); |
- if (!lock) { |
- PR_DELETE(mon); |
- return 0; |
- } |
+ if (mon == NULL) { |
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
+ return NULL; |
+ } |
- cvar = PR_NewCondVar(lock); |
- if (!cvar) { |
- PR_DestroyLock(lock); |
- PR_DELETE(mon); |
- return 0; |
- } |
- mon->cvar = cvar; |
- mon->name = NULL; |
- } |
+ rv = _PR_InitLock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ if (rv != PR_SUCCESS) |
+ goto error1; |
+ |
+ mon->owner = NULL; |
+ |
+ rv = _PR_InitCondVar(&mon->entryCV, &mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ if (rv != PR_SUCCESS) |
+ goto error2; |
+ |
+ rv = _PR_InitCondVar(&mon->waitCV, &mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ if (rv != PR_SUCCESS) |
+ goto error3; |
+ |
+ mon->notifyTimes = 0; |
+ mon->entryCount = 0; |
+ mon->name = NULL; |
return mon; |
+ |
+error3: |
+ _PR_FreeCondVar(&mon->entryCV); |
+error2: |
+ _PR_FreeLock(&mon->lock); |
+error1: |
+ PR_Free(mon); |
+ return NULL; |
} |
PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) |
@@ -51,9 +109,14 @@ |
*/ |
PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) |
{ |
- PR_DestroyLock(mon->cvar->lock); |
- PR_DestroyCondVar(mon->cvar); |
- PR_DELETE(mon); |
+ PR_ASSERT(mon != NULL); |
+ _PR_FreeCondVar(&mon->waitCV); |
+ _PR_FreeCondVar(&mon->entryCV); |
+ _PR_FreeLock(&mon->lock); |
+#if defined(DEBUG) |
+ memset(mon, 0xaf, sizeof(PRMonitor)); |
+#endif |
+ PR_Free(mon); |
} |
/* |
@@ -61,12 +124,28 @@ |
*/ |
PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) |
{ |
- if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) { |
- mon->entryCount++; |
- } else { |
- PR_Lock(mon->cvar->lock); |
- mon->entryCount = 1; |
+ PRThread *me = _PR_MD_CURRENT_THREAD(); |
+ PRStatus rv; |
+ |
+ PR_ASSERT(mon != NULL); |
+ PR_Lock(&mon->lock); |
+ if (mon->entryCount != 0) { |
+ if (mon->owner == me) |
+ goto done; |
+ while (mon->entryCount != 0) { |
+ rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ } |
} |
+ /* and now I have the monitor */ |
+ PR_ASSERT(mon->notifyTimes == 0); |
+ PR_ASSERT(mon->owner == NULL); |
+ mon->owner = me; |
+ |
+done: |
+ mon->entryCount += 1; |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
} |
/* |
@@ -76,16 +155,28 @@ |
*/ |
PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon) |
{ |
- if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) { |
- mon->entryCount++; |
- return PR_TRUE; |
- } else { |
- if (PR_TestAndLock(mon->cvar->lock)) { |
- mon->entryCount = 1; |
- return PR_TRUE; |
- } |
+ PRThread *me = _PR_MD_CURRENT_THREAD(); |
+ PRStatus rv; |
+ |
+ PR_ASSERT(mon != NULL); |
+ PR_Lock(&mon->lock); |
+ if (mon->entryCount != 0) { |
+ if (mon->owner == me) |
+ goto done; |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ return PR_FALSE; |
} |
- return PR_FALSE; |
+ /* and now I have the monitor */ |
+ PR_ASSERT(mon->notifyTimes == 0); |
+ PR_ASSERT(mon->owner == NULL); |
+ mon->owner = me; |
+ |
+done: |
+ mon->entryCount += 1; |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ return PR_TRUE; |
} |
/* |
@@ -93,12 +184,36 @@ |
*/ |
PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) |
{ |
- if (mon->cvar->lock->owner != _PR_MD_CURRENT_THREAD()) { |
+ PRThread *me = _PR_MD_CURRENT_THREAD(); |
+ PRStatus rv; |
+ |
+ PR_ASSERT(mon != NULL); |
+ PR_Lock(&mon->lock); |
+ /* the entries should be > 0 and we'd better be the owner */ |
+ PR_ASSERT(mon->entryCount > 0); |
+ PR_ASSERT(mon->owner == me); |
+ if (mon->entryCount == 0 || mon->owner != me) |
+ { |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
return PR_FAILURE; |
} |
- if (--mon->entryCount == 0) { |
- return PR_Unlock(mon->cvar->lock); |
+ |
+ mon->entryCount -= 1; /* reduce by one */ |
+ if (mon->entryCount == 0) |
+ { |
+ /* and if it transitioned to zero - notify an entry waiter */ |
+ /* make the owner unknown */ |
+ mon->owner = NULL; |
+ if (mon->notifyTimes != 0) { |
+ _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); |
+ mon->notifyTimes = 0; |
+ } |
+ rv = PR_NotifyCondVar(&mon->entryCV); |
+ PR_ASSERT(rv == PR_SUCCESS); |
} |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
return PR_SUCCESS; |
} |
@@ -108,17 +223,29 @@ |
*/ |
PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon) |
{ |
- return (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) ? |
- mon->entryCount : 0; |
+ PRThread *me = _PR_MD_CURRENT_THREAD(); |
+ PRStatus rv; |
+ PRIntn count = 0; |
+ |
+ PR_Lock(&mon->lock); |
+ if (mon->owner == me) |
+ count = mon->entryCount; |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ return count; |
} |
-/* |
-** If the current thread is in |mon|, this assertion is guaranteed to |
-** succeed. Otherwise, the behavior of this function is undefined. |
-*/ |
PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor *mon) |
{ |
- PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mon->cvar->lock); |
+#if defined(DEBUG) || defined(FORCE_PR_ASSERT) |
+ PRStatus rv; |
+ |
+ PR_Lock(&mon->lock); |
+ PR_ASSERT(mon->entryCount != 0 && |
+ mon->owner == _PR_MD_CURRENT_THREAD()); |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+#endif |
} |
/* |
@@ -135,25 +262,50 @@ |
** |
** Returns PR_FAILURE if the caller has not locked the lock associated |
** with the condition variable. |
-** This routine can return PR_PENDING_INTERRUPT if the waiting thread |
+** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread |
** has been interrupted. |
*/ |
PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks) |
{ |
- PRUintn entryCount; |
- PRStatus status; |
- PRThread *me = _PR_MD_CURRENT_THREAD(); |
+ PRStatus rv; |
+ PRUint32 saved_entries; |
+ PRThread *saved_owner; |
- if (mon->cvar->lock->owner != me) return PR_FAILURE; |
+ PR_ASSERT(mon != NULL); |
+ PR_Lock(&mon->lock); |
+ /* the entries better be positive */ |
+ PR_ASSERT(mon->entryCount > 0); |
+ /* and it better be owned by us */ |
+ PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD()); /* XXX return failure */ |
- entryCount = mon->entryCount; |
+ /* tuck these away 'till later */ |
+ saved_entries = mon->entryCount; |
mon->entryCount = 0; |
+ saved_owner = mon->owner; |
+ mon->owner = NULL; |
+ /* If we have pending notifies, post them now. */ |
+ if (mon->notifyTimes != 0) { |
+ _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes); |
+ mon->notifyTimes = 0; |
+ } |
+ rv = PR_NotifyCondVar(&mon->entryCV); |
+ PR_ASSERT(rv == PR_SUCCESS); |
- status = _PR_WaitCondVar(me, mon->cvar, mon->cvar->lock, ticks); |
+ rv = PR_WaitCondVar(&mon->waitCV, ticks); |
+ PR_ASSERT(rv == PR_SUCCESS); |
- mon->entryCount = entryCount; |
+ while (mon->entryCount != 0) { |
+ rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ } |
+ PR_ASSERT(mon->notifyTimes == 0); |
+ /* reinstate the interesting information */ |
+ mon->entryCount = saved_entries; |
+ mon->owner = saved_owner; |
- return status; |
+ rv = PR_Unlock(&mon->lock); |
+ PR_ASSERT(rv == PR_SUCCESS); |
+ return rv; |
} |
/* |
@@ -163,9 +315,7 @@ |
*/ |
PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) |
{ |
- PRThread *me = _PR_MD_CURRENT_THREAD(); |
- if (mon->cvar->lock->owner != me) return PR_FAILURE; |
- PR_NotifyCondVar(mon->cvar); |
+ _PR_PostNotifyToMonitor(mon, PR_FALSE); |
return PR_SUCCESS; |
} |
@@ -176,9 +326,7 @@ |
*/ |
PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) |
{ |
- PRThread *me = _PR_MD_CURRENT_THREAD(); |
- if (mon->cvar->lock->owner != me) return PR_FAILURE; |
- PR_NotifyAllCondVar(mon->cvar); |
+ _PR_PostNotifyToMonitor(mon, PR_TRUE); |
return PR_SUCCESS; |
} |
@@ -188,10 +336,9 @@ |
{ |
PRUint32 nb; |
- if (mon->cvar->lock->owner) { |
+ if (mon->owner) { |
nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld", |
- mon, mon->cvar->lock->owner->id, |
- mon->cvar->lock->owner, mon->entryCount); |
+ mon, mon->owner->id, mon->owner, mon->entryCount); |
} else { |
nb = PR_snprintf(buf, buflen, "[%p]", mon); |
} |