void Lock::CollectionLock::relockWithMode(LockMode mode, Lock::DBLock& dbLock ) { if (supportsDocLocking() || enableCollectionLocking) { _lockState->unlock(_id); } dbLock.relockWithMode( mode ); if (supportsDocLocking() || enableCollectionLocking) { _lockState->lock(_id, mode); } }
void Lock::CollectionLock::relockAsDatabaseExclusive(Lock::DBLock& dbLock) { if (supportsDocLocking() || enableCollectionLocking) { _lockState->unlock(_id); } dbLock.relockWithMode(MODE_X); if (supportsDocLocking() || enableCollectionLocking) { // don't need the lock, but need something to unlock in the destructor _lockState->lock(_id, MODE_IX); } }
bool PlanYieldPolicy::yield(RecordFetcher* fetcher) { invariant(_planYielding); OperationContext* opCtx = _planYielding->getOpCtx(); invariant(opCtx); // All YIELD_AUTO plans will get here eventually when the elapsed tracker triggers that it's // time to yield. Whether or not we will actually yield (doc-level locking systems won't), // we need to check if this operation has been interrupted. Throws if the interrupt flag is // set. opCtx->checkForInterrupt(); if (supportsDocLocking()) { // Doc-level locking is supported, so no need to release locks. return true; } // No need to yield if the collection is NULL. if (NULL == _planYielding->collection()) { return true; } _planYielding->saveState(); // Release and reacquire locks. QueryYield::yieldAllLocks(opCtx, fetcher); _elapsedTracker.resetLastTime(); return _planYielding->restoreState(opCtx); }
void AutoGetCollectionForRead::_init() { massert(28535, "need a non-empty collection name", !_nss.coll().empty()); // TODO: Client::Context legacy, needs to be removed _txn->getCurOp()->ensureStarted(); _txn->getCurOp()->setNS(_nss.toString()); // Lock both the DB and the collection (DB is locked in the constructor), because this is // necessary in order to to shard version checking. const ResourceId resId(RESOURCE_COLLECTION, _nss); const LockMode collLockMode = supportsDocLocking() ? MODE_IS : MODE_S; invariant(LOCK_OK == _txn->lockState()->lock(resId, collLockMode)); // Shard version check needs to be performed under the collection lock ensureShardVersionOKOrThrow(_nss); // At this point, we are locked in shared mode for the database by the DB lock in the // constructor, so it is safe to load the DB pointer. _db = dbHolder().get(_txn, _nss.db()); if (_db != NULL) { // TODO: Client::Context legacy, needs to be removed _txn->getCurOp()->enter(_nss.toString().c_str(), _db->getProfilingLevel()); _coll = _db->getCollection(_txn, _nss); } }
Lock::CollectionLock::CollectionLock(Locker* lockState, StringData ns, LockMode mode) : _id(RESOURCE_COLLECTION, ns), _lockState(lockState) { massert(28538, "need a non-empty collection name", nsIsFull(ns)); dassert(_lockState->isDbLockedForMode(nsToDatabaseSubstring(ns), isSharedLockMode(mode) ? MODE_IS : MODE_IX)); if (supportsDocLocking()) { _lockState->lock(_id, mode); } else { _lockState->lock(_id, isSharedLockMode(mode) ? MODE_S : MODE_X); } }
void WorkingSetCommon::prepareForSnapshotChange(WorkingSet* workingSet) { dassert(supportsDocLocking()); for (WorkingSet::iterator it = workingSet->begin(); it != workingSet->end(); ++it) { if (it->state == WorkingSetMember::LOC_AND_IDX) { it->isSuspicious = true; } else if (it->state == WorkingSetMember::LOC_AND_UNOWNED_OBJ) { // We already have the data so convert directly to owned state. it->obj.setValue(it->obj.value().getOwned()); it->state = WorkingSetMember::LOC_AND_OWNED_OBJ; } } }
void Lock::DBLock::lockDB() { const bool isRead = (_mode == newlm::MODE_S || _mode == newlm::MODE_IS); TrackLockAcquireTime a(isRead ? 'r' : 'w'); _lockState->lockGlobal(isRead ? newlm::MODE_IS : newlm::MODE_IX); if (supportsDocLocking() || isRead) { _lockState->lock(_id, _mode); } else { _lockState->lock(_id, newlm::MODE_X); } resetTime(); }
Lock::CollectionLock::CollectionLock(Locker* lockState, const StringData& ns, LockMode mode) : _id(RESOURCE_COLLECTION, ns), _lockState(lockState) { const bool isRead = (mode == MODE_S || mode == MODE_IS); massert(28538, "need a non-empty collection name", nsIsFull(ns)); dassert(_lockState->isDbLockedForMode(nsToDatabaseSubstring(ns), isRead ? MODE_IS : MODE_IX)); if (supportsDocLocking()) { _lockState->lock(_id, mode); } else if (enableCollectionLocking) { _lockState->lock(_id, isRead ? MODE_S : MODE_X); } }
void Lock::DBLock::relockWithMode(LockMode newMode) { // 2PL would delay the unlocking invariant(!_locker->inAWriteUnitOfWork()); // Not allowed to change global intent invariant(!isSharedLockMode(_mode) || isSharedLockMode(newMode)); _locker->unlock(_id); _mode = newMode; if (supportsDocLocking() || enableCollectionLocking) { invariant(LOCK_OK == _locker->lock(_id, _mode)); } else { invariant(LOCK_OK == _locker->lock(_id, isSharedLockMode(_mode) ? MODE_S : MODE_X)); } }
Lock::CollectionLock::CollectionLock(Locker* lockState, StringData ns, LockMode mode, Date_t deadline) : _id(RESOURCE_COLLECTION, ns), _result(LOCK_INVALID), _lockState(lockState) { massert(28538, "need a non-empty collection name", nsIsFull(ns)); dassert(_lockState->isDbLockedForMode(nsToDatabaseSubstring(ns), isSharedLockMode(mode) ? MODE_IS : MODE_IX)); LockMode actualLockMode = mode; if (!supportsDocLocking()) { actualLockMode = isSharedLockMode(mode) ? MODE_S : MODE_X; } _result = _lockState->lock(_id, actualLockMode, deadline); invariant(_result == LOCK_OK || deadline != Date_t::max()); }
void Lock::DBRead::unlockDB() { if (supportsDocLocking()) { if (nsIsFull(_ns)) { const newlm::ResourceId resIdCollection(newlm::RESOURCE_COLLECTION, _ns); _lockState->unlock(resIdCollection); } } const StringData db = nsToDatabaseSubstring(_ns); const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db); _lockState->unlock(resIdDb); // The last release reports time the lock was held if (_lockState->unlockGlobal()) { recordTime(); } }
Lock::DBLock::DBLock(Locker* lockState, const StringData& db, LockMode mode) : ScopedLock(lockState), _id(RESOURCE_DATABASE, db), _lockState(lockState), _mode(mode) { massert(28539, "need a valid database name", !db.empty() && nsIsDbOnly(db)); const bool isRead = (_mode == MODE_S || _mode == MODE_IS); _lockState->lockGlobal(isRead ? MODE_IS : MODE_IX); if (supportsDocLocking() || enableCollectionLocking) { _lockState->lock(_id, _mode); } else { _lockState->lock(_id, isRead ? MODE_S : MODE_X); } }
Lock::CollectionLock::CollectionLock(Locker* lockState, const StringData& ns, newlm::LockMode mode) : _id(newlm::RESOURCE_COLLECTION, ns), _lockState(lockState) { const bool isRead = (mode == newlm::MODE_S || mode == newlm::MODE_IS); dassert(!ns.empty()); dassert(nsIsFull(ns)); dassert(_lockState->isLockHeldForMode(newlm::ResourceId(newlm::RESOURCE_DATABASE, nsToDatabaseSubstring(ns)), isRead ? newlm::MODE_IS : newlm::MODE_IX)); if (supportsDocLocking()) { _lockState->lock(_id, mode); } else { _lockState->lock(_id, isRead ? newlm::MODE_S : newlm::MODE_X); } }
void WorkingSetCommon::prepareForSnapshotChange(WorkingSet* workingSet) { dassert(supportsDocLocking()); for (auto id : workingSet->getAndClearYieldSensitiveIds()) { if (workingSet->isFree(id)) { continue; } // We may see the same member twice, so anything we do here should be idempotent. WorkingSetMember* member = workingSet->get(id); if (member->getState() == WorkingSetMember::LOC_AND_IDX) { member->isSuspicious = true; } else if (member->getState() == WorkingSetMember::LOC_AND_OBJ) { // Need to make sure that the data is owned, as underlying storage can change during a // yield. member->makeObjOwned(); } } }
void Lock::DBLock::relockWithMode(const LockMode newMode) { const bool wasRead = (_mode == MODE_S || _mode == MODE_IS); const bool isRead = (newMode == MODE_S || newMode == MODE_IS); invariant (!_lockState->inAWriteUnitOfWork()); // 2PL would delay the unlocking invariant(!wasRead || isRead); // Not allowed to change global intent _lockState->unlock(_id); _mode = newMode; if (supportsDocLocking() || enableCollectionLocking) { _lockState->lock(_id, _mode); dassert(_lockState->isLockHeldForMode(_id, _mode)); } else { LockMode effectiveMode = isRead ? MODE_S : MODE_X; _lockState->lock(_id, effectiveMode); dassert(_lockState->isLockHeldForMode(_id, effectiveMode)); } }
void run() { if ( supportsDocLocking() ) { return; } auto_ptr<PlanExecutor> run(getCollscan()); BSONObj obj; // Read some of it. for (int i = 0; i < 10; ++i) { ASSERT_EQUALS(PlanExecutor::ADVANCED, run->getNext(&obj, NULL)); ASSERT_EQUALS(i, obj["foo"].numberInt()); } // Register it. run->saveState(); registerExecutor(run.get()); // At this point it's safe to yield. forceYield would do that. Let's now simulate some // stuff going on in the yield. // Delete some data, namely the next 2 things we'd expect. _client.remove(ns(), BSON("foo" << 10)); _client.remove(ns(), BSON("foo" << 11)); // At this point, we're done yielding. We recover our lock. // Unregister the runner. deregisterExecutor(run.get()); // And clean up anything that happened before. run->restoreState(&_opCtx); // Make sure that the runner moved forward over the deleted data. We don't see foo==10 // or foo==11. for (int i = 12; i < N(); ++i) { ASSERT_EQUALS(PlanExecutor::ADVANCED, run->getNext(&obj, NULL)); ASSERT_EQUALS(i, obj["foo"].numberInt()); } ASSERT_EQUALS(PlanExecutor::IS_EOF, run->getNext(&obj, NULL)); }
void WorkingSetCommon::prepareForSnapshotChange(WorkingSet* workingSet) { if (!supportsDocLocking()) { // Non doc-locking storage engines use invalidations, so we don't need to examine the // buffered working set ids. But we do need to clear the set of ids in order to keep our // memory utilization in check. workingSet->getAndClearYieldSensitiveIds(); return; } for (auto id : workingSet->getAndClearYieldSensitiveIds()) { if (workingSet->isFree(id)) { continue; } // We may see the same member twice, so anything we do here should be idempotent. WorkingSetMember* member = workingSet->get(id); if (member->getState() == WorkingSetMember::RID_AND_IDX) { member->isSuspicious = true; } } }
void Lock::DBRead::lockDB() { TrackLockAcquireTime a('r'); const StringData db = nsToDatabaseSubstring(_ns); const newlm::ResourceId resIdDb(newlm::RESOURCE_DATABASE, db); _lockState->lockGlobal(newlm::MODE_IS); if (supportsDocLocking()) { _lockState->lock(resIdDb, newlm::MODE_IS); if (nsIsFull(_ns)) { const newlm::ResourceId resIdCollection(newlm::RESOURCE_COLLECTION, _ns); _lockState->lock(resIdCollection, newlm::MODE_IS); } } else { _lockState->lock(resIdDb, newlm::MODE_S); } resetTime(); }
Lock::DBLock::DBLock(Locker* locker, StringData db, LockMode mode) : _id(RESOURCE_DATABASE, db), _locker(locker), _mode(mode), _globalLock(locker, isSharedLockMode(_mode) ? MODE_IS : MODE_IX, UINT_MAX) { massert(28539, "need a valid database name", !db.empty() && nsIsDbOnly(db)); // Need to acquire the flush lock _locker->lockMMAPV1Flush(); if (supportsDocLocking() || enableCollectionLocking) { // The check for the admin db is to ensure direct writes to auth collections // are serialized (see SERVER-16092). if ((_id == resourceIdAdminDB) && !isSharedLockMode(_mode)) { _mode = MODE_X; } invariant(LOCK_OK == _locker->lock(_id, _mode)); } else { invariant(LOCK_OK == _locker->lock(_id, isSharedLockMode(_mode) ? MODE_S : MODE_X)); } }
void Lock::OplogIntentWriteLock::serializeIfNeeded() { if (!supportsDocLocking() && !_serialized) { oplogSerialization.lock(); _serialized = true; } }
Lock::CollectionLock::~CollectionLock() { if (supportsDocLocking() || enableCollectionLocking) { _lockState->unlock(_id); } }