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, self)); if (!lock->locked || !pthread_equal(lock->owner, self)) return PR_FAILURE; lock->locked = PR_FALSE; if (0 == lock->notified.length) /* shortcut */ { rv = pthread_mutex_unlock(&lock->mutex); PR_ASSERT(0 == rv); } else pt_PostNotifies(lock, PR_TRUE); #if defined(DEBUG) pt_debug.locks_released += 1; #endif return PR_SUCCESS; } /* PR_Unlock */
PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) { PRIntn rv; PRThread *thred = PR_GetCurrentThread(); PR_ASSERT(cvar != NULL); /* We'd better be locked */ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); PR_ASSERT(PR_TRUE == cvar->lock->locked); /* and it better be by us */ PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); if (_PT_THREAD_INTERRUPTED(thred)) goto aborted; /* * The thread waiting is used for PR_Interrupt */ thred->waiting = cvar; /* this is where we're waiting */ /* * 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 != cvar->lock->notified.length) pt_PostNotifies(cvar->lock, PR_FALSE); /* * We're surrendering the lock, so clear out the locked field. */ cvar->lock->locked = PR_FALSE; if (timeout == PR_INTERVAL_NO_TIMEOUT) rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex); else rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout); /* We just got the lock back - this better be empty */ PR_ASSERT(PR_FALSE == cvar->lock->locked); cvar->lock->locked = PR_TRUE; cvar->lock->owner = pthread_self(); PR_ASSERT(0 == cvar->lock->notified.length); thred->waiting = NULL; /* and now we're not */ if (_PT_THREAD_INTERRUPTED(thred)) goto aborted; if (rv != 0) { _PR_MD_MAP_DEFAULT_ERROR(rv); return PR_FAILURE; } return PR_SUCCESS; aborted: PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); thred->state &= ~PT_THREAD_ABORTED; return PR_FAILURE; } /* PR_WaitCondVar */
PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) { PRStatus rv; PRInt16 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 */ PR_ASSERT(mon->entryCount > 0); /* and it better be by us */ PR_ASSERT(pthread_equal(mon->owner, pthread_self())); /* tuck these away 'till later */ saved_entries = mon->entryCount; 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); /* reinstate the intresting information */ mon->entryCount = saved_entries; _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); return rv; } /* PR_Wait */
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); return PR_SUCCESS; } /* PR_NotifyAll */
PR_IMPLEMENT(PRStatus) PRP_NakedWait( PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout) { PRIntn rv; PR_ASSERT(cvar != NULL); /* XXX do we really want to assert this in a naked wait? */ PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex)); if (timeout == PR_INTERVAL_NO_TIMEOUT) rv = pthread_cond_wait(&cvar->cv, &ml->mutex); else rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout); if (rv != 0) { _PR_MD_MAP_DEFAULT_ERROR(rv); return PR_FAILURE; } return PR_SUCCESS; } /* PRP_NakedWait */
/* * Notifies just get posted to the protecting mutex. The * actual notification is done when the lock is released so that * MP systems don't contend for a lock that they can't have. */ static void pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast) { PRIntn index = 0; _PT_Notified *notified = &cvar->lock->notified; PR_ASSERT(PR_TRUE == cvar->lock->locked); PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); while (1) { for (index = 0; index < notified->length; ++index) { if (notified->cv[index].cv == cvar) { if (broadcast) notified->cv[index].times = -1; else if (-1 != notified->cv[index].times) notified->cv[index].times += 1; goto finished; /* we're finished */ } } /* if not full, enter new CV in this array */ if (notified->length < PT_CV_NOTIFIED_LENGTH) break; /* if there's no link, create an empty array and link it */ if (NULL == notified->link) notified->link = PR_NEWZAP(_PT_Notified); notified = notified->link; } /* A brand new entry in the array */ (void)PR_AtomicIncrement(&cvar->notify_pending); 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(PRStatus) PR_ExitMonitor(PRMonitor *mon) { pthread_t self = pthread_self(); 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 */ PR_ASSERT(pthread_equal(mon->owner, self)); if (!pthread_equal(mon->owner, self)) 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); } return PR_SUCCESS; } /* PR_ExitMonitor */