void LockerImpl::_yieldFlushLockForMMAPV1() { if (!inAWriteUnitOfWork()) { invariant(unlock(resourceIdMMAPV1Flush)); invariant(LOCK_OK == lock(resourceIdMMAPV1Flush, getLockMode(resourceIdGlobal), UINT_MAX)); } }
bool LockerImpl<IsForMMAPV1>::isCollectionLockedForMode(StringData ns, LockMode mode) const { invariant(nsIsFull(ns)); if (isW()) return true; if (isR() && isSharedLockMode(mode)) return true; const NamespaceString nss(ns); const ResourceId resIdDb(RESOURCE_DATABASE, nss.db()); LockMode dbMode = getLockMode(resIdDb); if (!shouldConflictWithSecondaryBatchApplication()) return true; switch (dbMode) { case MODE_NONE: return false; case MODE_X: return true; case MODE_S: return isSharedLockMode(mode); case MODE_IX: case MODE_IS: { const ResourceId resIdColl(RESOURCE_COLLECTION, ns); return isLockHeldForMode(resIdColl, mode); } break; case LockModesCount: break; } invariant(false); return false; }
LockResult LockerImpl::lock(const ResourceId& resId, LockMode mode, unsigned timeoutMs) { _notify.clear(); _lock.lock(); LockRequest* request = _find(resId); if (request == NULL) { request = new LockRequest(); request->initNew(resId, this, &_notify); _requests.insert(LockRequestsPair(resId, request)); } else { invariant(request->recursiveCount > 0); request->notify = &_notify; } _lock.unlock(); // Methods on the Locker class are always called single-threadly, so it is safe to release // the spin lock, which protects the Locker here. The only thing which could alter the // state of the request is deadlock detection, which however would synchronize on the // LockManager calls. LockResult result = globalLockManagerPtr->lock(resId, request, mode); if (result == LOCK_WAITING) { // Under MMAP V1 engine a deadlock can occur if a thread goes to sleep waiting on DB // lock, while holding the flush lock, so it has to be released. This is only correct // to do if not in a write unit of work. bool unlockedFlushLock = false; if (!inAWriteUnitOfWork() && (resId != resourceIdGlobal) && (resId != resourceIdMMAPV1Flush) && (resId != resourceIdLocalDB)) { invariant(unlock(resourceIdMMAPV1Flush)); unlockedFlushLock = true; } // Do the blocking outside of the flush lock (if not in a write unit of work) result = _notify.wait(timeoutMs); if (unlockedFlushLock) { // We cannot obey the timeout here, because it is not correct to return from the // lock request with the flush lock released. invariant(LOCK_OK == lock(resourceIdMMAPV1Flush, getLockMode(resourceIdGlobal), UINT_MAX)); } } if (result != LOCK_OK) { // Can only be LOCK_TIMEOUT, because the lock manager does not return any other errors // at this point. Could be LOCK_DEADLOCK, when deadlock detection is implemented. invariant(result == LOCK_TIMEOUT); invariant(_unlockAndUpdateRequestsList(resId, request)); } return result; }
void LockerImpl<IsForMMAPV1>::lockMMAPV1Flush() { if (!IsForMMAPV1) return; // The flush lock always has a reference count of 1, because it is dropped at the end of // each write unit of work in order to allow the flush thread to run. See the comments in // the header for information on how the MMAP V1 journaling system works. LockRequest* globalLockRequest = _requests.find(resourceIdGlobal).objAddr(); if (globalLockRequest->recursiveCount == 1) { invariant(LOCK_OK == lock(resourceIdMMAPV1Flush, _getModeForMMAPV1FlushLock())); } dassert(getLockMode(resourceIdMMAPV1Flush) == _getModeForMMAPV1FlushLock()); }
char LockerImpl::threadState() const { switch (getLockMode(resourceIdGlobal)) { case MODE_IS: return 'r'; case MODE_IX: return 'w'; case MODE_S: return 'R'; case MODE_X: return 'W'; case MODE_NONE: return '\0'; } invariant(false); }
LockResult LockerImpl::lockGlobal(LockMode mode, unsigned timeoutMs) { LockRequest* request = _find(resourceIdGlobal); if (request != NULL) { // No upgrades on the GlobalLock are allowed until we can handle deadlocks. invariant(request->mode >= mode); } else { // Global lock should be the first lock on the operation invariant(_requests.empty()); } Timer timer; LockResult globalLockResult = lock(resourceIdGlobal, mode, timeoutMs); if (globalLockResult != LOCK_OK) { invariant(globalLockResult == LOCK_TIMEOUT); return globalLockResult; } // Obey the requested timeout const unsigned elapsedTimeMs = timer.millis(); const unsigned remainingTimeMs = elapsedTimeMs < timeoutMs ? (timeoutMs - elapsedTimeMs) : 0; if (request == NULL) { // Special-handling for MMAP V1. LockResult flushLockResult = lock(resourceIdMMAPV1Flush, getLockMode(resourceIdGlobal), remainingTimeMs); if (flushLockResult != LOCK_OK) { invariant(flushLockResult == LOCK_TIMEOUT); invariant(unlock(resourceIdGlobal)); return flushLockResult; } } return LOCK_OK; }
bool LockerImpl<IsForMMAPV1>::isLockHeldForMode(ResourceId resId, LockMode mode) const { return isModeCovered(mode, getLockMode(resId)); }
LockResult LockerImpl<IsForMMAPV1>::lockGlobalComplete(Milliseconds timeout) { return lockComplete(resourceIdGlobal, getLockMode(resourceIdGlobal), timeout, false); }
bool LockerImpl<IsForMMAPV1>::isLocked() const { return getLockMode(resourceIdGlobal) != MODE_NONE; }
bool LockerImpl<IsForMMAPV1>::isR() const { return getLockMode(resourceIdGlobal) == MODE_S; }
LockResult LockerImpl<IsForMMAPV1>::lockGlobalComplete(OperationContext* opCtx, Date_t deadline) { return lockComplete(opCtx, resourceIdGlobal, getLockMode(resourceIdGlobal), deadline, false); }
bool LockerImpl::isR() const { return getLockMode(resourceIdGlobal) == MODE_S; }
bool LockerImpl::isLockHeldForMode(const ResourceId& resId, LockMode mode) const { return getLockMode(resId) >= mode; }