diff options
-rw-r--r-- | src/mongo/db/catalog/collection.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_cursor_cache.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/concurrency/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_mgr.cpp | 1334 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_mgr.h | 741 | ||||
-rw-r--r-- | src/mongo/db/concurrency/lock_mgr_test.cpp | 894 | ||||
-rw-r--r-- | src/mongo/db/exec/update.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/operation_context.h | 6 | ||||
-rw-r--r-- | src/mongo/db/operation_context_impl.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/operation_context_impl.h | 7 | ||||
-rw-r--r-- | src/mongo/db/operation_context_noop.h | 4 | ||||
-rw-r--r-- | src/mongo/db/startup_warnings_mongod.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp | 1 |
13 files changed, 6 insertions, 3020 deletions
diff --git a/src/mongo/db/catalog/collection.cpp b/src/mongo/db/catalog/collection.cpp index 635b1159db6..b61374ee82f 100644 --- a/src/mongo/db/catalog/collection.cpp +++ b/src/mongo/db/catalog/collection.cpp @@ -41,7 +41,6 @@ #include "mongo/db/curop.h" #include "mongo/db/catalog/collection_catalog_entry.h" #include "mongo/db/catalog/database.h" -#include "mongo/db/concurrency/lock_mgr.h" #include "mongo/db/catalog/index_create.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/operation_context.h" @@ -409,7 +408,6 @@ namespace mongo { // Broadcast the mutation so that query results stay correct. _cursorCache.invalidateDocument(loc, INVALIDATION_MUTATION); - ExclusiveResourceLock lk(txn->getTransaction(), *(size_t*)&loc); return _recordStore->updateWithDamages( txn, loc, damangeSource, damages ); } diff --git a/src/mongo/db/catalog/collection_cursor_cache.cpp b/src/mongo/db/catalog/collection_cursor_cache.cpp index 057f6ddcd17..eec9a5c6469 100644 --- a/src/mongo/db/catalog/collection_cursor_cache.cpp +++ b/src/mongo/db/catalog/collection_cursor_cache.cpp @@ -388,18 +388,14 @@ namespace mongo { } void CollectionCursorCache::registerExecutor( PlanExecutor* exec ) { - if (!useExperimentalDocLocking) { - SimpleMutex::scoped_lock lk(_mutex); - const std::pair<ExecSet::iterator, bool> result = _nonCachedExecutors.insert(exec); - invariant(result.second); // make sure this was inserted - } + SimpleMutex::scoped_lock lk(_mutex); + const std::pair<ExecSet::iterator, bool> result = _nonCachedExecutors.insert(exec); + invariant(result.second); // make sure this was inserted } void CollectionCursorCache::deregisterExecutor( PlanExecutor* exec ) { - if (!useExperimentalDocLocking) { - SimpleMutex::scoped_lock lk(_mutex); - _nonCachedExecutors.erase(exec); - } + SimpleMutex::scoped_lock lk(_mutex); + _nonCachedExecutors.erase(exec); } ClientCursor* CollectionCursorCache::find( CursorId id, bool pin ) { diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript index 2bff2e6c489..b1c5e5e8578 100644 --- a/src/mongo/db/concurrency/SConscript +++ b/src/mongo/db/concurrency/SConscript @@ -6,7 +6,6 @@ env.Library( target='lock_mgr', source=[ 'd_concurrency.cpp', - 'lock_mgr.cpp', 'lock_mgr_new.cpp', 'lock_stat.cpp', 'lock_state.cpp', diff --git a/src/mongo/db/concurrency/lock_mgr.cpp b/src/mongo/db/concurrency/lock_mgr.cpp deleted file mode 100644 index 56363f8b739..00000000000 --- a/src/mongo/db/concurrency/lock_mgr.cpp +++ /dev/null @@ -1,1334 +0,0 @@ -/** - * Copyright (C) 2014 MongoDB Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/platform/basic.h" - -#include "mongo/db/concurrency/lock_mgr.h" - -#include <boost/thread/locks.hpp> -#include <sstream> - -#include "mongo/base/init.h" -#include "mongo/db/server_parameters.h" -#include "mongo/util/assert_util.h" -#include "mongo/util/log.h" -#include "mongo/util/timer.h" - -using std::endl; -using std::exception; -using std::map; -using std::multiset; -using std::set; -using std::string; -using std::stringstream; -using std::vector; - -namespace mongo { - - /*---------- Utility functions ----------*/ - - namespace { - - bool isExclusive(const LockMode& mode) { - return kExclusive == mode; - } - - bool isShared(const LockMode& mode) { - return kShared == mode; - } - - bool isCompatible(const LockMode& mode1, const LockMode& mode2) { - return mode1==mode2 && isShared(mode1); - } - - bool hasConflict(const LockManager::ResourceStatus& status) { - return LockManager::kResourceConflict == status || - LockManager::kResourceUpgradeConflict == status || - LockManager::kResourcePolicyConflict == status; - } - } // namespace - - /*---------- AbortException functions ----------*/ - - const char* LockManager::AbortException::what() const throw() { return "AbortException"; } - - /*---------- LockStats functions ----------*/ - string LockManager::LockStats::toString() const { - stringstream result; - result << "----- LockManager Stats -----" << endl - << "\ttotal requests: " << getNumRequests() << endl - << "\t# pre-existing: " << getNumPreexistingRequests() << endl - << "\t# same: " << getNumSameRequests() << endl - << "\t# times blocked: " << getNumBlocks() << endl - << "\t# ms blocked: " << getNumMillisBlocked() << endl - << "\t# deadlocks: " << getNumDeadlocks() << endl - << "\t# downgrades: " << getNumDowngrades() << endl - << "\t# upgrades: " << getNumUpgrades() << endl - ; - return result.str(); - } - - LockManager::LockStats& LockManager::LockStats::operator+=(const LockStats& other) { - _numRequests += other._numRequests; - _numPreexistingRequests += other._numPreexistingRequests; - _numSameRequests += other._numSameRequests; - _numBlocks += other._numBlocks; - _numDeadlocks += other._numDeadlocks; - _numBlocks += other._numBlocks; - _numDowngrades += other._numDowngrades; - _numUpgrades += other._numUpgrades; - _numMillisBlocked += other._numMillisBlocked; - return *this; - } - - /*---------- Transaction functions ----------*/ - Transaction::Transaction(unsigned txId, int priority) - : _txId(txId) -#ifdef REGISTER_TRANSACTION - , _txSlice(LockManager::partitionTransaction(txId)) -#endif - , _priority(priority) - , _state((0==txId) ? kInvalid : kActive) - , _locks(NULL) { } - - Transaction::~Transaction() { - - } - - Transaction* Transaction::setTxIdOnce(unsigned txId) { - if (0 == _txId) { - _txId = txId; - _state = kActive; - } - - return this; - } - - bool Transaction::operator<(const Transaction& other) { - return _txId < other._txId; - } - - int Transaction::getPriority() const { return _priority; } - void Transaction::setPriority(int newPriority) { _priority = newPriority; } - - void Transaction::removeLock(LockRequest* lr) { - if (lr->nextOfTransaction) { - lr->nextOfTransaction->prevOfTransaction = lr->prevOfTransaction; - } - if (lr->prevOfTransaction) { - lr->prevOfTransaction->nextOfTransaction = lr->nextOfTransaction; - } - else { - _locks = lr->nextOfTransaction; - } - lr->nextOfTransaction = NULL; - lr->prevOfTransaction = NULL; - if (lr->heapAllocated) delete lr; - } - - void Transaction::addLock(LockRequest* lr) { - lr->nextOfTransaction = _locks; - - if (_locks) { - _locks->prevOfTransaction = lr; - } - _locks = lr; - } - - void Transaction::_addWaiter(Transaction* waiter) { - _waiters.insert(waiter); - _waiters.insert(waiter->_waiters.begin(), waiter->_waiters.end()); - } - - string Transaction::toString() const { - stringstream result; - result << "<xid:" << _txId -#ifdef REGISTER_TRANSACTIONS - << ",slice:" << _txSlice -#endif - << ",priority:" << _priority - << ",state:" << ((kActive == _state) ? "active" : "completed"); - - result << ",locks: {"; - bool firstLock=true; - for (LockRequest* nextLock = _locks; nextLock; nextLock=nextLock->nextOfTransaction) { - if (firstLock) firstLock=false; - else result << ","; - result << nextLock->toString(); - } - result << "}"; - - result << ">,waiters: {"; - bool firstWaiter=true; - for (multiset<Transaction*>::const_iterator nextWaiter = _waiters.begin(); - nextWaiter != _waiters.end(); ++nextWaiter) { - if (firstWaiter) firstWaiter=false; - else result << ","; - result << (*nextWaiter)->_txId; - } - result << "}>"; - return result.str(); - } - - - /*---------- LockRequest functions ----------*/ - - - LockRequest::LockRequest(const ResourceId& resId, - const LockMode& mode, - Transaction* tx, - bool heapAllocated) - : requestor(tx) - , mode(mode) - , resId(resId) - , slice(LockManager::partitionResource(resId)) - , count(1) - , sleepCount(0) - , heapAllocated(heapAllocated) - , nextOnResource(NULL) - , prevOnResource(NULL) - , nextOfTransaction(NULL) - , prevOfTransaction(NULL) { } - - - LockRequest::~LockRequest() { - verify(NULL == nextOfTransaction); - verify(NULL == prevOfTransaction); - verify(NULL == nextOnResource); - verify(NULL == prevOnResource); - } - - bool LockRequest::matches(const Transaction* tx, - const LockMode& mode, - const ResourceId& resId) const { - return - this->requestor == tx && - this->mode == mode && - this->resId == resId; - } - - string LockRequest::toString() const { - stringstream result; - result << "<xid:" << requestor->_txId - << ",mode:" << mode - << ",resId:" << resId - << ",count:" << count - << ",sleepCount:" << sleepCount - << ">"; - return result.str(); - } - - bool LockRequest::isBlocked() const { - return sleepCount > 0; - } - - bool LockRequest::shouldAwake() { - return 0 == --sleepCount; - } - - void LockRequest::insert(LockRequest* lr) { - lr->prevOnResource = this->prevOnResource; - lr->nextOnResource = this; - - if (this->prevOnResource) { - this->prevOnResource->nextOnResource = lr; - } - this->prevOnResource = lr; - } - - void LockRequest::append(LockRequest* lr) { - lr->prevOnResource = this; - lr->nextOnResource = this->nextOnResource; - - if (this->nextOnResource) { - this->nextOnResource->prevOnResource = lr; - } - this->nextOnResource = lr; - } - - /*---------- LockManager public functions (mutex guarded) ---------*/ - - - // This startup parameter enables experimental document-level locking features - // It should be removed once full document-level locking is checked-in. - MONGO_EXPORT_STARTUP_SERVER_PARAMETER(useExperimentalDocLocking, bool, false); - - static LockManager* _singleton = NULL; - - MONGO_INITIALIZER(InstantiateLockManager)(InitializerContext* context) { - _singleton = new LockManager(); - return Status::OK(); - } - - LockManager& LockManager::getSingleton() { - return *_singleton; - } - - LockManager::LockManager(const Policy& policy) - : _policy(policy) - , _mutex() - , _shuttingDown(false) - , _millisToQuiesce(-1) - , _systemTransaction(new Transaction(0)) - , _numCurrentActiveReadRequests(0) - , _numCurrentActiveWriteRequests(0) - { } - - LockManager::~LockManager() { - delete _systemTransaction; - } - - void LockManager::shutdown(const unsigned& millisToQuiesce) { - boost::unique_lock<boost::mutex> lk(_mutex); - -#ifdef DONT_ALLOW_CHANGE_TO_QUIESCE_PERIOD - // XXX not sure whether we want to allow multiple shutdowns - // in order to change quiesce period? - if (_shuttingDown) { - return; // already in shutdown, don't extend quiescence(?) - } -#endif - - _shuttingDown = true; - _millisToQuiesce = millisToQuiesce; - _timer.millisReset(); - } - - LockManager::Policy LockManager::getPolicy() const { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - return _policy; - } - - Transaction* LockManager::getPolicySetter() const { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - return _policySetter; - } - - void LockManager::setPolicy(Transaction* tx, const Policy& policy, Notifier* notifier) { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - - if (policy == _policy) return; - - _policySetter = tx; - Policy oldPolicy = _policy; - _policy = policy; - - // if moving away from {READERS,WRITERS}_ONLY, awaken requests that were pending - // - if (kPolicyReadersOnly == oldPolicy || kPolicyWritersOnly == oldPolicy) { - - // Awaken requests that were blocked on the old policy. - // iterate over TxIds blocked on kReservedTxId (these are blocked on policy) - - for (multiset<Transaction*>::iterator nextWaiter = _systemTransaction->_waiters.begin(); - nextWaiter != _systemTransaction->_waiters.end(); ++nextWaiter) { - - // iterate over the locks acquired by the blocked transactions - for (LockRequest* nextLock = (*nextWaiter)->_locks; nextLock; - nextLock = nextLock->nextOfTransaction) { - if (nextLock->isBlocked() && nextLock->shouldAwake()) { - - // each transaction can only be blocked by one request at time - // this one must be due to policy that's now changed - nextLock->lock.notify_one(); - } - } - } - _systemTransaction->_waiters.clear(); - } - - // if moving to {READERS,WRITERS}_ONLY, block until no incompatible locks - if (kPolicyReadersOnly == policy || kPolicyWritersOnly == policy) { - unsigned (LockManager::*numBlockers)() const = (kPolicyReadersOnly == policy) - ? &LockManager::_numActiveWrites - : &LockManager::_numActiveReads; - - if ((this->*numBlockers)() > 0) { - if (notifier) { - (*notifier)(_systemTransaction); - } - do { - _policyLock.wait(lk); - } while ((this->*numBlockers)() > 0); - } - } - } - - void LockManager::acquireLock(LockRequest* lr, Notifier* notifier) { - if (!useExperimentalDocLocking) { - return; - } - - invariant(lr); - - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - } - - // don't accept requests from aborted transactions - if (Transaction::kAborted == lr->requestor->_state) { - throw AbortException(); - } - boost::unique_lock<boost::mutex> lk(_resourceMutexes[lr->slice]); - - LockRequest* queue = _resourceLocks[lr->slice][lr->resId]; - LockRequest* conflictPosition = queue; - ResourceStatus status = _getConflictInfo(lr->requestor, lr->mode, lr->resId, lr->slice, - queue, conflictPosition); - if (kResourceAcquired == status) { return; } - - // add lock request to requesting transaction's list - lr->requestor->addLock(lr); - - _acquireInternal(lr, queue, conflictPosition, status, notifier, lk); - _incStatsForMode(lr->mode); - } - - void LockManager::acquire(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - Notifier* notifier) { - if (kReservedResourceId == resId || !useExperimentalDocLocking) { - return; - } - - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - } - - // don't accept requests from aborted transactions - if (Transaction::kAborted == requestor->_state) { - throw AbortException(); - } - unsigned slice = partitionResource(resId); - boost::unique_lock<boost::mutex> lk(_resourceMutexes[slice]); - - LockRequest* queue = _resourceLocks[slice][resId]; - LockRequest* conflictPosition = queue; - ResourceStatus status = _getConflictInfo(requestor, mode, resId, slice, - queue, conflictPosition); - if (kResourceAcquired == status) { return; } - - LockRequest* lr = new LockRequest(resId, mode, requestor, true); - - // add lock request to requesting transaction's list - lr->requestor->addLock(lr); - - _acquireInternal(lr, queue, conflictPosition, status, notifier, lk); - _incStatsForMode(mode); - } - - int LockManager::acquireOne(Transaction* requestor, - const LockMode& mode, - const vector<ResourceId>& resources, - Notifier* notifier) { - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(requestor); - } - - // don't accept requests from aborted transactions - if (Transaction::kAborted == requestor->_state) { - throw AbortException(); - } - - if (resources.empty()) { return -1; } - - // acquire the first available recordId - for (unsigned ix=0; ix < resources.size(); ix++) { - ResourceId resId = resources[ix]; - unsigned slice = partitionResource(resId); - bool isAvailable = false; - { - boost::unique_lock<boost::mutex> lk(_resourceMutexes[slice]); - isAvailable = _isAvailable(requestor, mode, resId, slice); - } - if (isAvailable) { - acquire(requestor, mode, resId, notifier); - return ix; - } - } - - // sigh. none of the records are currently available. wait on the first. - acquire(requestor, mode, resources[0], notifier); - return 0; - } - - LockManager::LockStatus LockManager::releaseLock(LockRequest* lr) { - if (!useExperimentalDocLocking) return kLockNotFound; - invariant(lr); - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(lr->requestor); - } - boost::unique_lock<boost::mutex> lk(_resourceMutexes[lr->slice]); - _decStatsForMode(lr->mode); - return _releaseInternal(lr); - } - - LockManager::LockStatus LockManager::release(const Transaction* holder, - const LockMode& mode, - const ResourceId& resId) { - if (kReservedResourceId == resId || !useExperimentalDocLocking) { - return kLockNotFound; - } - - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(holder); - } - unsigned slice = partitionResource(resId); - boost::unique_lock<boost::mutex> lk(_resourceMutexes[slice]); - - LockRequest* lr; - LockStatus status = _findLock(holder, mode, resId, slice, lr); - if (kLockFound != status) { - return status; // error, resource wasn't acquired in this mode by holder - } - _decStatsForMode(mode); - return _releaseInternal(lr); - } - -#if 0 - /* - * release all resource acquired by a transaction, returning the count - */ - size_t LockManager::release(Transaction* holder) { - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(holder); - } - - TxLockMap::iterator lockIdsHeld = _xaLocks.find(holder); - if (lockIdsHeld == _xaLocks.end()) { return 0; } - size_t numLocksReleased = 0; - for (set<LockId>::iterator nextLockId = lockIdsHeld->second.begin(); - nextLockId != lockIdsHeld->second.end(); ++nextLockId) { - _releaseInternal(*nextLockId); - - _decStatsForMode(_locks[*nextLockId]->mode); - - if ((kPolicyWritersOnly == _policy && 0 == _stats.numActiveReads()) || - (kPolicyReadersOnly == _policy && 0 == _stats.numActiveWrites())) { - _policyLock.notify_one(); - } - numLocksReleased++; - } - return numLocksReleased; - } -#endif - void LockManager::abort(Transaction* goner) { - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(goner); - } - _abortInternal(goner); - } - - LockManager::LockStats LockManager::getStats() const { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(); - - LockStats result; - for (unsigned ix=0; ix < kNumResourcePartitions; ix++) { - result += _stats[ix]; - } - return result; - } - - string LockManager::toString() const { -// boost::unique_lock<boost::mutex> lk(_mutex); -#ifdef DONT_CARE_ABOUT_DEBUG_EVEN_WHEN_SHUTTING_DOWN - // seems like we might want to allow toString for debug during shutdown? - _throwIfShuttingDown(); -#endif - stringstream result; - result << "Policy: "; - switch(_policy) { - case kPolicyFirstCome: - result << "FirstCome"; - break; - case kPolicyReadersFirst: - result << "ReadersFirst"; - break; - case kPolicyOldestTxFirst: - result << "OldestFirst"; - break; - case kPolicyBlockersFirst: - result << "BiggestBlockerFirst"; - break; - case kPolicyReadersOnly: - result << "ReadersOnly"; - break; - case kPolicyWritersOnly: - result << "WritersOnly"; - break; - } - result << endl; - - if (_shuttingDown) - result << " shutting down in " << _millisToQuiesce - _timer.millis(); - - result << "\t_resourceLocks:" << endl; - bool firstResource=true; - result << "resources=" << ": {"; - for (unsigned slice=0; slice < kNumResourcePartitions; ++slice) { - for (map<ResourceId, LockRequest*>::const_iterator nextResource = _resourceLocks[slice].begin(); - nextResource != _resourceLocks[slice].end(); ++nextResource) { - if (firstResource) firstResource=false; - else result << ", "; - result << nextResource->first << ": {"; - bool firstLock=true; - for (LockRequest* nextLock = nextResource->second; - nextLock; nextLock=nextLock->nextOnResource) { - if (firstLock) firstLock=false; - else result << ", "; - result << nextLock->toString(); - } - result << "}"; - } - } - result << "}" << endl; -#ifdef REGISTER_TRANSACTIONS - result << "\tTransactions:" << endl; - bool firstTx=true; - for (unsigned jx=0; jx < kNumTransactionPartitions; ++jx) { - for (set<Transaction*>::const_iterator nextTx = _activeTransactions[jx].begin(); - nextTx != _activeTransactions[jx].end(); ++nextTx) { - if (firstTx) firstTx=false; - else result << ", "; - result << "\t\t" << (*nextTx)->toString(); - } - } -#endif - return result.str(); - } - - bool LockManager::isLocked(const Transaction* holder, - const LockMode& mode, - const ResourceId& resId) const { - if (!useExperimentalDocLocking) { - return false; - } - { - boost::unique_lock<boost::mutex> lk(_mutex); - _throwIfShuttingDown(holder); - } - - LockRequest* unused=NULL; - return kLockFound == _findLock(holder, mode, resId, partitionResource(resId), unused); - } - - unsigned LockManager::partitionResource(const ResourceId& resId) { - // when resIds are DiskLocs, their low-order bits are mostly zero - // so add up nibbles as cheap hash - size_t resIdValue = resId; - size_t resIdHash = 0; - size_t mask = 0xf; - for (unsigned ix=0; ix < 2*sizeof(size_t); ++ix) { - resIdHash += (resIdValue >> ix*4) & mask; - } - return resIdHash % kNumResourcePartitions; - } -#ifdef REGISTER_TRANSACTIONS - unsigned LockManager::partitionTransaction(unsigned xid) { - return xid % kNumTransactionPartitions; - } -#endif - - void LockManager::_push_back(LockRequest* lr) { - LockRequest* nextLock = _resourceLocks[lr->slice][lr->resId]; - if (NULL == nextLock) { - _resourceLocks[lr->slice][lr->resId] = lr; - return; - } - - while (nextLock->nextOnResource) { - nextLock = nextLock->nextOnResource; - } - - nextLock->append(lr); - } - - void LockManager::_removeFromResourceQueue(LockRequest* lr) { - if (lr->nextOnResource) { - lr->nextOnResource->prevOnResource = lr->prevOnResource; - } - if (lr->prevOnResource) { - lr->prevOnResource->nextOnResource = lr->nextOnResource; - } - else if (NULL == lr->nextOnResource) { - _resourceLocks[lr->slice].erase(lr->resId); - } - else { - _resourceLocks[lr->slice][lr->resId] = lr->nextOnResource; - } - lr->nextOnResource = NULL; - lr->prevOnResource = NULL; - } - - /*---------- LockManager private functions (alphabetical) ----------*/ - - /* - * release resources acquired by a transaction about to abort, notifying - * any waiters that they can retry their resource acquisition. cleanup - * and throw an AbortException. - */ - void LockManager::_abortInternal(Transaction* goner) { - - goner->_state = Transaction::kAborted; - - if (NULL == goner->_locks) { - // unusual, but possible to abort a transaction with no locks - throw AbortException(); - } - - // release all resources acquired by this transaction - // notifying any waiters that they can continue - // - LockRequest* nextLock = goner->_locks; - while (nextLock) { - // releaseInternal deletes nextLock, so get the next ptr here - LockRequest* newNextLock = nextLock->nextOfTransaction; - _releaseInternal(nextLock); - nextLock = newNextLock; - } - - // erase aborted transaction's waiters - goner->_waiters.clear(); - - throw AbortException(); - } - - LockManager::ResourceStatus LockManager::_getConflictInfo(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest* queue, - LockRequest*& conflictPosition) { - _stats[slice].incRequests(); - - if (queue) { _stats[slice].incPreexisting(); } - - ResourceStatus resourceStatus = _conflictExists(requestor, mode, resId, - slice, queue, conflictPosition); - if (kResourceAcquired == resourceStatus) { - _stats[slice].incSame(); - ++conflictPosition->count; - } - return resourceStatus; - } - - void LockManager::_acquireInternal(LockRequest* lr, - LockRequest* queue, - LockRequest* conflictPosition, - ResourceStatus resourceStatus, - Notifier* sleepNotifier, - boost::unique_lock<boost::mutex>& guard) { - - if (kResourceAvailable == resourceStatus) { - if (!conflictPosition) - _push_back(lr); - else if (conflictPosition == queue) { - lr->nextOnResource = _resourceLocks[lr->slice][lr->resId]; - _resourceLocks[lr->slice][lr->resId] = lr; - } - else { - conflictPosition->prevOnResource->nextOnResource = lr; - lr->nextOnResource = conflictPosition; - lr->prevOnResource = conflictPosition->prevOnResource; - } - - _addWaiters(lr, conflictPosition, NULL); - return; - } - - // some type of conflict, insert after confictPosition - - verify(conflictPosition || - kResourcePolicyConflict == resourceStatus || - kResourceUpgradeConflict == resourceStatus); - - if (conflictPosition) { - conflictPosition = conflictPosition->nextOnResource; - } - - if (kResourceUpgradeConflict == resourceStatus) { - if (conflictPosition) - conflictPosition->insert(lr); - else - _push_back(lr); - } - else { - _addLockToQueueUsingPolicy(lr, queue, conflictPosition); - } - -#ifdef VERIFY_LOCK_MANAGER - if (isExclusive(mode)) { - for (LockRequest* nextFollower = conflictPosition; - nextFollower; nextFollower=nextFollower->nextOnResource) { - if (nextFollower->requestor == requestor) continue; - verify(nextFollower->isBlocked()); - } - } -#endif - // set remaining incompatible requests as lr's waiters - _addWaiters(lr, conflictPosition, NULL); - - - // call the sleep notification function once - if (NULL != sleepNotifier) { - // XXX should arg be xid of blocker? - (*sleepNotifier)(lr->requestor); - } - - _stats[lr->slice].incBlocks(); - - // this loop typically executes once - do { - // set up for future deadlock detection add requestor to blockers' waiters - // - for (LockRequest* nextBlocker = queue; nextBlocker != conflictPosition; - nextBlocker=nextBlocker->nextOnResource) { - if (nextBlocker == lr) {break;} - if (nextBlocker->requestor == lr->requestor) {continue;} - if (isCompatible(nextBlocker->mode, lr->mode)) {continue;} - nextBlocker->requestor->_addWaiter(lr->requestor); - ++lr->sleepCount; - } - if (kResourcePolicyConflict == resourceStatus) { - // to facilitate waking once the policy reverts, add requestor to system's waiters - _systemTransaction->_addWaiter(lr->requestor); - ++lr->sleepCount; - } - - // wait for blocker to release - while (lr->isBlocked()) { - Timer timer; - lr->lock.wait(guard); - _stats[lr->slice].incTimeBlocked(timer.millis()); - } - - queue = conflictPosition = _resourceLocks[lr->slice][lr->resId]; - resourceStatus = _conflictExists(lr->requestor, lr->mode, lr->resId, lr->slice, - queue, conflictPosition); - } while (hasConflict(resourceStatus)); - } - - /* - * called only when there are conflicting LockRequests - * positions a lock request (lr) in a queue at or after position - * also adds remaining requests in queue as lr's waiters - * for subsequent deadlock detection - */ - void LockManager::_addLockToQueueUsingPolicy(LockRequest* lr, - LockRequest* queue, - LockRequest*& position) { - - if (position == NULL) { - _push_back(lr); - return; - } - - // use lock request's transaction's priority if specified - int txPriority = lr->requestor->getPriority(); - if (txPriority > 0) { - for (; position; position=position->nextOnResource) { - if (txPriority > position->requestor->getPriority()) { - // add in front of request with lower priority that is either - // compatible, or blocked - // - position->insert(lr); - return; - } - } - _push_back(lr); - return; - } - else if (txPriority < 0) { - // for now, just push to end - // TODO: honor position of low priority requests - _push_back(lr); - } - - // use LockManager's default policy - switch (_policy) { - case kPolicyFirstCome: - _push_back(lr); - position = NULL; - return; - case kPolicyReadersFirst: - if (isExclusive(lr->mode)) { - _push_back(lr); - position = NULL; - return; - } - for (; position; position=position->nextOnResource) { - if (isExclusive(position->mode) && position->isBlocked()) { - // insert shared lock before first sleeping exclusive lock - position->insert(lr); - return; - } - } - break; - case kPolicyOldestTxFirst: - for (; position; position=position->nextOnResource) { - if (lr->requestor < position->requestor && - (isCompatible(lr->mode, position->mode) || position->isBlocked())) { - // smaller xid is older, so queue it before - position->insert(lr); - return; - } - } - break; - case kPolicyBlockersFirst: { - size_t lrNumWaiters = lr->requestor->_waiters.size(); - for (; position; position=position->nextOnResource) { - size_t nextRequestNumWaiters = position->requestor->_waiters.size(); - if (lrNumWaiters > nextRequestNumWaiters && - (isCompatible(lr->mode, position->mode) || position->isBlocked())) { - position->insert(lr); - return; - } - } - break; - } - default: - break; - } - - _push_back(lr); - position = NULL; - } - - void LockManager::_addWaiters(LockRequest* blocker, - LockRequest* nextLock, - LockRequest* lastLock) { - for (; nextLock != lastLock; nextLock=nextLock->nextOnResource) { - if (! isCompatible(blocker->mode, nextLock->mode)) { - if (nextLock->sleepCount > 0) { - blocker->requestor->_addWaiter(nextLock->requestor); - ++nextLock->sleepCount; - } - } - } - } - - bool LockManager::_comesBeforeUsingPolicy(const Transaction* requestor, - const LockMode& mode, - const LockRequest* oldRequest) const { - - // handle special policies - if (kPolicyReadersOnly == _policy && kShared == mode && oldRequest->isBlocked()) - return true; - if (kPolicyWritersOnly == _policy && kExclusive == mode && oldRequest->isBlocked()) - return true; - - if (requestor->getPriority() > - oldRequest->requestor->getPriority()) { - return true; - } - - switch (_policy) { - case kPolicyFirstCome: - return false; - case kPolicyReadersFirst: - return isShared(mode); - case kPolicyOldestTxFirst: - return requestor < oldRequest->requestor; - case kPolicyBlockersFirst: { - return requestor->_waiters.size() > oldRequest->requestor->_waiters.size(); - } - default: - return false; - } - } - - LockManager::ResourceStatus LockManager::_conflictExists(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest* queue, - LockRequest*& nextLock) { - - // handle READERS/kPolicyWritersOnly policy conflicts - if ((kPolicyReadersOnly == _policy && isExclusive(mode)) || - (kPolicyWritersOnly == _policy && isShared(mode))) { - - if (NULL == nextLock) { return kResourcePolicyConflict; } - - // position past the last active lock request on the queue - LockRequest* lastActivePosition = NULL; - for (; nextLock; nextLock = nextLock->nextOnResource) { - if (requestor == nextLock->requestor && mode == nextLock->mode) { - return kResourceAcquired; // already have the lock - } - if (! nextLock->isBlocked()) { - lastActivePosition = nextLock; - } - } - if (lastActivePosition) { - nextLock = lastActivePosition; - } - return kResourcePolicyConflict; - } - - // loop over the lock requests in the queue, looking for the 1st conflict - // normally, we'll leave the nextLock positioned at the 1st conflict - // if there is one, or the position (often the end) where we know there is no conflict. - // - // upgrades complicate this picture, because we want to position the iterator - // after all initial share locks. but we may not know whether an exclusived request - // is an upgrade until we look at all the initial share locks. - // - // so we record the position of the 1st conflict, but continue advancing the - // nextLock until we've seen all initial share locks. If none have - // the same Transaction as the exclusive request, we restore the position to 1st conflict - // - LockRequest* firstConflict = NULL; - set<Transaction*> sharedOwners; // all initial share lock owners - bool alreadyHadLock = false; // true if we see a lock with the same Txid - - for (; nextLock; nextLock=nextLock->nextOnResource) { - - if (nextLock->matches(requestor, mode, resId)) { - // if we're already on the queue, there's no conflict - return kResourceAcquired; - } - - if (requestor == nextLock->requestor) { - // an upgrade or downgrade request, can't conflict with ourselves - if (isShared(mode)) { - // downgrade - _stats[slice].incDowngrades(); - nextLock = nextLock->nextOnResource; - return kResourceAvailable; - } - - // upgrade - alreadyHadLock = true; - _stats[slice].incUpgrades(); - // position after initial readers - continue; - } - - if (isShared(nextLock->mode)) { - invariant(!nextLock->isBlocked() || kPolicyWritersOnly == _policy); - - sharedOwners.insert(nextLock->requestor); - - if (isExclusive(mode) && firstConflict == NULL) { - // if "lr" proves not to be an upgrade, restore this position later - firstConflict = nextLock; - } - // either there's no conflict yet, or we're not done checking for an upgrade - continue; - } - - // the next lock on the queue is an exclusive request - invariant(isExclusive(nextLock->mode)); - - if (alreadyHadLock) { - // bumped into something incompatible while up/down grading - if (isExclusive(mode)) { - // upgrading: bumped into another exclusive lock - if (sharedOwners.find(nextLock->requestor) != sharedOwners.end()) { - // the exclusive lock is also an upgrade, and it must - // be blocked, waiting for our original share lock to be released - // if we wait for its shared lock, we would deadlock - // - invariant(nextLock->isBlocked()); - _abortInternal(requestor); - } - - if (sharedOwners.empty()) { - // simple upgrade, queue in front of nextLockRequest, no conflict - return kResourceAvailable; - } - else { - // we have to wait for another shared lock before upgrading - return kResourceUpgradeConflict; - } - } - - // downgrading, bumped into an exclusive lock, blocked on our original - invariant (isShared(mode)); - invariant(nextLock->isBlocked()); - // lr will be inserted before nextLockRequest - return kResourceAvailable; - } - else if (firstConflict) { - // restore first conflict position - nextLock = firstConflict; - } - - // no conflict if nextLock is blocked and we come before - if (nextLock->isBlocked() && - _comesBeforeUsingPolicy(requestor, mode, nextLock)) { - return kResourceAvailable; - } - - // there's a conflict, check for deadlock - if (requestor->_waiters.find(nextLock->requestor) != requestor->_waiters.end()) { - // the transaction that would block requestor is already blocked by requestor - // if requestor waited for nextLockRequest, there would be a deadlock - // - _stats[slice].incDeadlocks(); - _abortInternal(requestor); - } - return kResourceConflict; - } - - // positioned to the end of the queue - if (alreadyHadLock && isExclusive(mode) && !sharedOwners.empty()) { - // upgrading, queue consists of requestor's earlier share lock - // plus other share lock. Must wait for the others to release - return kResourceUpgradeConflict; - } - else if (firstConflict) { - nextLock = firstConflict; - - if (_comesBeforeUsingPolicy(requestor, mode, nextLock)) { - return kResourceAvailable; - } - - // there's a conflict, check for deadlock - if (requestor->_waiters.find(nextLock->requestor) != requestor->_waiters.end()) { - // the transaction that would block requestor is already blocked by requestor - // if requestor waited for nextLockRequest, there would be a deadlock - // - _stats[slice].incDeadlocks(); - _abortInternal(requestor); - } - return kResourceConflict; - } - return kResourceAvailable; - } - - LockManager::LockStatus LockManager::_findLock(const Transaction* holder, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest*& outLock) const { - - outLock = NULL; // set invalid; - - // get iterator for resId's locks - map<ResourceId,LockRequest*>::const_iterator resLocks = _resourceLocks[slice].find(resId); - if (resLocks == _resourceLocks[slice].end()) { return kLockResourceNotFound; } - - // look for an existing lock request from holder in mode - for (LockRequest* nextLock = resLocks->second; - nextLock; nextLock=nextLock->nextOnResource) { - if (nextLock->requestor == holder && nextLock->mode == mode) { - outLock = nextLock; - return kLockFound; - } - } - return kLockModeNotFound; - } - - /* - * Used by acquireOne - * XXX: there's overlap between this, _conflictExists and _findLock - */ - bool LockManager::_isAvailable(const Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice) const { - - // check for exceptional policies - if (kPolicyReadersOnly == _policy && isExclusive(mode)) - return false; - else if (kPolicyWritersOnly == _policy && isShared(mode)) - return false; - - - // walk over the queue of previous requests for this ResourceId - for (const LockRequest* nextLock = _resourceLocks[slice].at(resId); - nextLock; nextLock = nextLock->nextOnResource) { - - if (nextLock->matches(requestor, mode, resId)) { - // we're already have this lock, if we're asking, we can't be asleep - invariant(! nextLock->isBlocked()); - return true; - } - - // no conflict if we're compatible - if (isCompatible(mode, nextLock->mode)) continue; - - // no conflict if nextLock is blocked and we come before - if (nextLock->isBlocked() && _comesBeforeUsingPolicy(requestor, mode, nextLock)) - return true; - - return false; // we're incompatible and would block - } - - // everything on the queue (if anything is on the queue) is compatible - return true; - } - - LockManager::LockStatus LockManager::_releaseInternal(LockRequest* lr) { - Transaction* holder = lr->requestor; - const LockMode& mode = lr->mode; - - if ((kPolicyWritersOnly == _policy && 0 == _numActiveReads()) || - (kPolicyReadersOnly == _policy && 0 == _numActiveWrites())) { - _policyLock.notify_one(); - } - - LockRequest* queue = _resourceLocks[lr->slice][lr->resId]; - if (NULL == queue) { - return kLockResourceNotFound; - } - - bool foundLock = false; - bool foundResource = false; - - LockRequest* nextLock = queue; - - // find the position of the lock to release in the queue - for(; !foundLock && nextLock; nextLock=nextLock->nextOnResource) { - if (lr != nextLock) { - if (nextLock->requestor == holder) { - foundResource = true; - } - } - else { - // this is our lock. - if (--nextLock->count > 0) { return kLockCountDecremented; } - - foundLock = true; - break; // don't increment nextLock again - } - } - - if (! foundLock) { - // can't release a lock that hasn't been acquired in the specified mode - return foundResource ? kLockModeNotFound : kLockResourceNotFound; - } - - if (isShared(mode)) { - // skip over any remaining shared requests. they can't be waiting for us. - for (; nextLock; nextLock=nextLock->nextOnResource) { - if (isExclusive(nextLock->mode)) { - break; - } - } - } - - // everything left on the queue potentially conflicts with the lock just - // released, unless it's an up/down-grade of that lock. So iterate, and - // when Transactions differ, decrement sleepCount, wake those with zero counts, and - // decrement their sleep counts, waking sleepers with zero counts, and - // cleanup state used for deadlock detection - - for (; nextLock; nextLock=nextLock->nextOnResource) { - LockRequest* nextSleeper = nextLock; - if (nextSleeper->requestor == holder) continue; - - invariant(nextSleeper->isBlocked()); - - // remove nextSleeper and its dependents from holder's waiters - - if (holder->_waiters.find(nextSleeper->requestor) != holder->_waiters.end()) { - // every sleeper should be among holders waiters, but a previous sleeper might have - // had the nextSleeper as a dependent as well, in which case nextSleeper was removed - // previously, hence the test for finding nextSleeper among holder's waiters - // - Transaction* sleepersTx = nextSleeper->requestor; - holder->_waiters.erase(holder->_waiters.find(sleepersTx)); - multiset<Transaction*>::iterator nextSleepersWaiter = sleepersTx->_waiters.begin(); - for(; nextSleepersWaiter != sleepersTx->_waiters.end(); ++nextSleepersWaiter) { - holder->_waiters.erase(*nextSleepersWaiter); - } - } - - // wake up sleepy heads - if (nextSleeper->shouldAwake()) { - nextSleeper->lock.notify_one(); - } - } - -#ifdef VERIFY_LOCK_MANAGER - if (holder->_waiters.empty()) { - for (set<Transaction*>::iterator nextTx = _activeTransactions.begin(); - nextTx != _activeTransactions.end(); ++nextTx) { - verify( (*nextTx)->_waiters.find(holder) == (*nextTx)->_waiters.end()); - } - } - - if (queue) { - verify(!queue->isBlocked()); - } -#endif - - // release the lock - _removeFromResourceQueue(lr); - holder->removeLock(lr); - - return kLockReleased; - } - - void LockManager::_throwIfShuttingDown(const Transaction* tx) const { - - if (_shuttingDown && (_timer.millis() >= _millisToQuiesce)) - -#ifdef LOCK_MANAGER_TRANSACTION_REGISTRATION - || - _activeTransactions[tx->txSlice].find(tx) == _activeTransactions[tx->txSlice].end())) -#endif - { - - throw AbortException(); // XXX should this be something else? ShutdownException? - } - } - -/*---------- ResourceLock functions ----------*/ - - ResourceLock::ResourceLock(LockManager& lm, - Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - LockManager::Notifier* notifier) - : _lm(lm) - , _lr(resId, mode, requestor) - { - _lm.acquireLock(&_lr, notifier); - } - - ResourceLock::~ResourceLock() { - _lm.releaseLock(&_lr); - } - -} // namespace mongo diff --git a/src/mongo/db/concurrency/lock_mgr.h b/src/mongo/db/concurrency/lock_mgr.h deleted file mode 100644 index a38b4f3aec7..00000000000 --- a/src/mongo/db/concurrency/lock_mgr.h +++ /dev/null @@ -1,741 +0,0 @@ -/** - * Copyright (C) 2014 MongoDB Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include <boost/thread/condition_variable.hpp> -#include <boost/thread/mutex.hpp> -#include <iterator> -#include <map> -#include <set> -#include <string> -#include <vector> - -#include "mongo/platform/atomic_word.h" -#include "mongo/platform/compiler.h" -#include "mongo/platform/cstdint.h" -#include "mongo/util/timer.h" - - -/* - * LockManager controls access to resources through two functions: acquire and release - * - * Resources can be databases, collections, oplogs, records, btree-nodes, key-value pairs, - * forward/backward pointers, or anything at all that can be unambiguously identified. - * - * Resources are acquired by Transactions for either shared or exclusive use. If an - * acquisition request conflicts with a pre-existing use of a resource, the requesting - * transaction will block until the original conflicting requests and any new conflicting - * requests have been released. - * - * Contention for a resource is resolved by a LockingPolicy, which determines which blocked - * resource requests to awaken when the blocker releases the resource. - * - */ - -namespace mongo { - - // Defined in lock_mgr.cpp - extern bool useExperimentalDocLocking; - - class ResourceId { - public: - ResourceId() : _rid(0) { } - ResourceId(size_t rid) : _rid(rid) { } - bool operator<(const ResourceId& other) const { return _rid < other._rid; } - bool operator==(const ResourceId& other) const { return _rid == other._rid; } - operator size_t() const { return _rid; } - - private: - uint64_t _rid; - }; - static const ResourceId kReservedResourceId = 0; - - - /** - * LockModes: shared and exclusive - */ - enum LockMode { - kShared = 0, // conflicts only with kExclusive - kExclusive = 1, // conflicts with all lock modes - kInvalid - }; - - - class Transaction; - - /** - * Data structure used to record a resource acquisition request - */ - class LockRequest { - public: - LockRequest(const ResourceId& resId, - const LockMode& mode, - Transaction* requestor, - bool heapAllocated = false); - - - ~LockRequest(); - - bool matches(const Transaction* tx, - const LockMode& mode, - const ResourceId& resId) const; - - bool isBlocked() const; - bool shouldAwake(); - - std::string toString() const; - - // insert/append in resource chain - void insert(LockRequest* lr); - void append(LockRequest* lr); - - // transaction that made this request (not owned) - Transaction* requestor; - - // shared or exclusive use - const LockMode mode; - - // resource requested - const ResourceId resId; - - // a hash of resId modulo kNumResourcePartitions - // used to mitigate cost of mutex locking - unsigned slice; - - // number of times a tx requested resource in this mode - // lock request will be deleted when count goes to 0 - size_t count; - - // number of existing things blocking this request - // usually preceding requests on the queue, but also policy - size_t sleepCount; - - // ResourceLock classes (see below) using the RAII pattern - // allocate LockRequests on the stack. - bool heapAllocated; - - // lock requests are chained by their resource - LockRequest* nextOnResource; - LockRequest* prevOnResource; - - // lock requests are also chained by their requesting transaction - LockRequest* nextOfTransaction; - LockRequest* prevOfTransaction; - - // used for waiting and waking - boost::condition_variable lock; - }; - - /** - * Data structure used to describe resource requestors, - * used for conflict resolution, deadlock detection, and abort - */ - class Transaction { - public: - Transaction(unsigned txId=0, int priority=0); - ~Transaction(); - - /** - * transactions are identified by an id - */ - Transaction* setTxIdOnce(unsigned txId); - unsigned getTxId() const { return _txId; } - - /** - * override default LockManager's default Policy for a transaction. - * - * positive priority moves transaction's resource requests toward the front - * of the queue, behind only those requests with higher priority. - * - * negative priority moves transaction's resource requests toward the back - * of the queue, ahead of only those requests with lower priority. - * - * zero priority uses the LockManager's default Policy - */ - void setPriority(int newPriority); - int getPriority() const; - - /** - * maintain the queue of lock requests made by the transaction - */ - void removeLock(LockRequest* lr); - void addLock(LockRequest* lr); - - /** - * should be age of the transaction. currently using txId as a proxy. - */ - bool operator<(const Transaction& other); - - /** - * for debug - */ - std::string toString() const; - - private: - friend class LockManager; - friend class LockRequest; - - void _addWaiter(Transaction* waiter); - - /** - * it might be useful to reject lock manager requests from inactive TXs. - */ - enum TxState { - kInvalid, - kActive, - kAborted, - kCommitted - }; - - // uniquely identify the transaction - unsigned _txId; -#ifdef REGISTER_TRANSACTIONS - // for mutex parallelism while handling transactions - unsigned _txSlice; -#endif - - // transaction priorities: - // 0 => neutral, use LockManager's default _policy - // + => high, queue forward - // - => low, queue back - // - int _priority; - - // LockManager doesnt accept new requests from completed transactions - TxState _state; - - // For cleanup and abort processing, references all LockRequests made by a transaction - LockRequest* _locks; - - // For deadlock detection: the set of transactions blocked by another transaction - // NB: a transaction can only be directly waiting for a single resource/transaction - // but to facilitate deadlock detection, if T1 is waiting for T2 and T2 is waiting - // for T3, then both T1 and T2 are listed as T3's waiters. - // - // This is a multiset to handle some obscure situations. If T1 has upgraded or downgraded - // its lock on a resource, it has two lock requests. If T2 then requests exclusive - // access to the same resource, it must wait for BOTH T1's locks to be relased. - // - // the max size of the set is ~2*number-concurrent-transactions. the set is only - // consulted/updated when there's a lock conflict. When there are many more documents - // than transactions, the set will usually be empty. - // - std::multiset<Transaction*> _waiters; - }; - - /** - * LockManager is used to control access to resources. Usually a singleton. For deadlock detection - * all resources used by a set of transactions that could deadlock, should use one LockManager. - * - * Primary functions are: - * acquire - acquire a resource for shared or exclusive use; may throw Abort. - * release - release a resource previously acquired for shared/exclusive use - */ - class LockManager { - public: - - /** - * thrown primarily when deadlocks are detected, or when LockManager::abort is called. - * also thrown when LockManager requests are made during shutdown. - */ - class AbortException : public std::exception { - public: - const char* what() const throw (); - }; - - /** - * Used to decide which blocked requests to honor first when resource becomes available - */ - enum Policy { - kPolicyFirstCome, // wake the first blocked request in arrival order - kPolicyReadersFirst, // wake the first blocked read request(s) - kPolicyOldestTxFirst, // wake the blocked request with the lowest TxId - kPolicyBlockersFirst, // wake the blocked request which is itself the most blocking - kPolicyReadersOnly, // block write requests (used during fsync) - kPolicyWritersOnly // block read requests - }; - - /** - * returned by ::conflictExists, called from acquire - */ - enum ResourceStatus { - kResourceAcquired, // requested resource was already acquired, increment count - kResourceAvailable, // requested resource is available. no waiting - kResourceConflict, // requested resource is in use by another transaction - kResourcePolicyConflict, // requested mode blocked by READER/kPolicyWritersOnly policy - kResourceUpgradeConflict // requested resource was previously acquired for shared use - // now requested for exclusive use, but there are other shared - // users, so this request must wait. - }; - - /** - * returned by find_lock(), and release() and, mostly for testing - * explains why a lock wasn't found or released - */ - enum LockStatus { - kLockFound, // found a matching lock request - kLockReleased, // released requested lock - kLockCountDecremented, // decremented lock count, but didn't release - kLockNotFound, // specific lock not found - kLockResourceNotFound, // no locks on the resource - kLockModeNotFound // locks on the resource, but not of the specified mode - }; - - - /** - * A Notifier is used to do something just before an acquisition request blocks. - * - * XXX: should perhaps define Notifier as a functor so C++11 lambda's match - * for the test.cpp, it was convenient for the call operator to access private - * state, which is why we're using the class formulation for now - */ - class Notifier { - public: - virtual ~Notifier() { } - virtual void operator()(const Transaction* blocker) = 0; - }; - - /** - * Tracks locking statistics. - */ - class LockStats { - public: - LockStats() - : _numRequests(0) - , _numPreexistingRequests(0) - , _numSameRequests(0) - , _numBlocks(0) - , _numDeadlocks(0) - , _numDowngrades(0) - , _numUpgrades(0) - , _numMillisBlocked(0) { } - - void incRequests() { _numRequests++; } - void incPreexisting() { _numPreexistingRequests++; } - void incSame() { _numSameRequests++; } - void incBlocks() { _numBlocks++; } - void incDeadlocks() { _numDeadlocks++; } - void incDowngrades() { _numDowngrades++; } - void incUpgrades() { _numUpgrades++; } - void incTimeBlocked(size_t numMillis ) { _numMillisBlocked += numMillis; } - - size_t getNumRequests() const { return _numRequests; } - size_t getNumPreexistingRequests() const { return _numPreexistingRequests; } - size_t getNumSameRequests() const { return _numSameRequests; } - size_t getNumBlocks() const { return _numBlocks; } - size_t getNumDeadlocks() const { return _numDeadlocks; } - size_t getNumDowngrades() const { return _numDowngrades; } - size_t getNumUpgrades() const { return _numUpgrades; } - size_t getNumMillisBlocked() const { return _numMillisBlocked; } - - LockStats& operator+=(const LockStats& other); - std::string toString() const; - - private: - // total number of resource requests. >= number or resources requested. - size_t _numRequests; - - // the number of times a resource was requested when there - // was a pre-existing request (possibly compatible) - size_t _numPreexistingRequests; - - // the number of times a transaction requested the same resource in the - // same mode while already holding a lock on that resource - size_t _numSameRequests; - - // the number of times a resource request blocked. This is usually - // because of a conflicting pre-existing request, but could be because - // of a policy like READERS_ONLY - size_t _numBlocks; - - size_t _numDeadlocks; - size_t _numDowngrades; - size_t _numUpgrades; - - // aggregates time requests spent blocked. - size_t _numMillisBlocked; - }; - - - public: - - /** - * Singleton factory - retrieves a common instance of LockManager - */ - static LockManager& getSingleton(); - - /** - * It's possibly useful to allow multiple LockManagers for non-overlapping sets - * of resources, so the constructor is left public. Eventually we may want - * to enforce a singleton pattern. - */ - explicit LockManager(const Policy& policy=kPolicyFirstCome); - ~LockManager(); - - /** - * Change the current Policy. For READERS/kPolicyWritersOnly, this - * call may block until all current writers/readers have released their locks. - */ - void setPolicy(Transaction* tx, const Policy& policy, Notifier* notifier = NULL); - - /** - * Get the current policy - */ - Policy getPolicy() const; - - /** - * Who set the current policy. Of use when the Policy is ReadersOnly - * and we want to find out who is blocking a writer. - */ - Transaction* getPolicySetter() const; - - /** - * Initiate a shutdown, specifying a period of time to quiesce. - * - * During this period, existing transactions can continue to acquire resources, - * but new transaction requests will throw AbortException. - * - * After quiescing, any new requests will throw AbortException - */ - void shutdown(const unsigned& millisToQuiesce = 1000); - - - /** - * acquire a resource in a mode. - * can throw AbortException - */ - void acquire(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - Notifier* notifier = NULL); - - /** - * acquire a designated lock. usually called from RAII objects - * that have allocated their lock on the stack. May throw AbortException. - */ - void acquireLock(LockRequest* request, Notifier* notifier = NULL); - - /** - * for bulk operations: - * acquire one of a vector of ResourceIds in a mode, - * hopefully without blocking, return index of - * acquired ResourceId, or -1 if vector was empty - */ - int acquireOne(Transaction* requestor, - const LockMode& mode, - const std::vector<ResourceId>& records, - Notifier* notifier = NULL); - - /** - * release a ResourceId. - * The mode here is just the mode that applies to the resId - */ - LockStatus release(const Transaction* holder, - const LockMode& mode, - const ResourceId& resId); - - /** - * releases the lock returned by acquire. should perhaps replace above? - */ - LockStatus releaseLock(LockRequest* request); - - /** - * release all resources acquired by a transaction - * returns number of locks released - */ - size_t release(const Transaction* holder); - - /** - * called internally for deadlock - * possibly called publicly to stop a long transaction - * also used for testing - */ - MONGO_COMPILER_NORETURN void abort(Transaction* goner); - - /** - * returns a copy of the stats that exist at the time of the call - */ - LockStats getStats() const; - - /** - * slices the space of ResourceIds and TransactionIds into - * multiple partitions that can be separately guarded to - * spread the cost of mutex locking. - */ - static unsigned partitionResource(const ResourceId& resId); - static unsigned partitionTransaction(unsigned txId); - - - - // --- for testing and logging - - std::string toString() const; - - /** - * test whether a Transaction has locked a ResourceId in a mode. - * most callers should use acquireOne instead - */ - bool isLocked(const Transaction* holder, - const LockMode& mode, - const ResourceId& resId) const; - - private: // alphabetical - - /** - * called by public ::abort and internally upon deadlock - * releases all locks acquired by goner, notify's any - * transactions that were waiting, then throws AbortException - */ - MONGO_COMPILER_NORETURN void _abortInternal(Transaction* goner); - - /** - * main workhorse for acquiring locks on resources, blocking - * or aborting on conflict - * - * throws AbortException on deadlock - */ - void _acquireInternal(LockRequest* lr, - LockRequest* queue, - LockRequest* conflictPosition, - ResourceStatus resourceStatus, - Notifier* notifier, - boost::unique_lock<boost::mutex>& guard); - - /** - * adds a conflicting lock request to the list of requests for a resource - * using the Policy. Called by acquireInternal - */ - void _addLockToQueueUsingPolicy(LockRequest* lr, - LockRequest* queue, - LockRequest*& position); - - /** - * when inserting a new lock request into the middle of a queue, - * add any remaining incompatible requests in the queue to the - * new lock request's set of waiters... for future deadlock detection - */ - void _addWaiters(LockRequest* blocker, - LockRequest* nextLock, - LockRequest* lastLock); - - /** - * returns true if a newRequest should be honored before an oldRequest according - * to the lockManager's policy. Used by acquire to decide whether a new share request - * conflicts with a previous upgrade-to-exclusive request that is blocked. - */ - bool _comesBeforeUsingPolicy(const Transaction* newReqTx, - const LockMode& newReqMode, - const LockRequest* oldReq) const; - - /** - * determine whether a resource request would conflict with an existing lock - * set the position to the first possible insertion point, which is usually - * the position of the first conflict, or the end of the queue, or to an existing lock - */ - ResourceStatus _conflictExists(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest* queue, - LockRequest*& position /* in/out */); - - /** - * looks for an existing LockRequest that matches the four input params - * if not found, sets outLid to zero and returns a reason, otherwise - * sets outLock to the LockRequest that matches and returns kLockFound - */ - LockStatus _findLock(const Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest*& outLock) const; - - /** - * @return status of requested resource id - * set conflictPosition on output if conflict - * update several status variables - */ - ResourceStatus _getConflictInfo(Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice, - LockRequest* queue, - LockRequest*& conflictPosition); - - /** - * returns true if acquire would return without waiting - * used by acquireOne - */ - bool _isAvailable(const Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - unsigned slice) const; - - /** - * maintain the resourceLocks queue - */ - void _push_back(LockRequest* lr); - void _removeFromResourceQueue(LockRequest* lr); - - - /** - * called by public ::release and internally by abort. - * assumes caller as acquired a mutex. - */ - LockStatus _releaseInternal(LockRequest* lr); - - /** - * called at start of public APIs, throws exception - * if quiescing period has expired, or if xid is new - */ - void _throwIfShuttingDown(const Transaction* tx=NULL) const; - - - private: - // support functions for changing policy to/from read/write only - - void _incStatsForMode(const LockMode& mode) { - kShared==mode ? - _numCurrentActiveReadRequests.fetchAndAdd(1) : - _numCurrentActiveWriteRequests.fetchAndAdd(1); - } - void _decStatsForMode(const LockMode& mode) { - kShared==mode ? - _numCurrentActiveReadRequests.fetchAndSubtract(1) : - _numCurrentActiveWriteRequests.fetchAndSubtract(1); - } - - unsigned _numActiveReads() const { return _numCurrentActiveReadRequests.loadRelaxed(); } - unsigned _numActiveWrites() const { return _numCurrentActiveWriteRequests.loadRelaxed(); } - - - private: - - // The Policy controls which requests should be honored first. This is - // used to guide the position of a request in a list of requests waiting for - // a resource. - // - // XXX At some point, we may want this to also guide the decision of which - // transaction to abort in case of deadlock. For now, the transaction whose - // request would lead to a deadlock is aborted. Since deadlocks are rare, - // careful choices may not matter much. - // - Policy _policy; - - // transaction which last set policy, for reporting cause of conflicts. not owned - Transaction* _policySetter; - - // synchronizes access to the lock manager, which is shared across threads - static const unsigned kNumResourcePartitions = 16; - mutable boost::mutex _resourceMutexes[kNumResourcePartitions]; - -#ifdef REGISTER_TRANSACTIONS - static const unsigned kNumTransactionPartitions = 16; - mutable boost::mutex _transactionMutexes[kNumTransactionPartitions]; -#endif - mutable boost::mutex _mutex; - - // for blocking when setting kPolicyReadersOnly or kPolicyWritersOnly policy - boost::condition_variable _policyLock; - - // only meaningful when _policy == SHUTDOWN - bool _shuttingDown; - int _millisToQuiesce; - Timer _timer; - - // Lists of lock requests associated with a resource, - // - // The lock-request lists have two sections. Some number (at least one) of requests - // at the front of a list are "active". All remaining lock requests are blocked by - // some earlier (not necessarily active) lock request, and are waiting. The order - // of lock request in the waiting section is determined by the LockPolicty. - // The order of lock request in the active/front portion of the list is irrelevant. - // - std::map<ResourceId, LockRequest*> _resourceLocks[kNumResourcePartitions]; - -#ifdef REGISTER_TRANSACTIONS - std::set<Transaction*> _activeTransactions[kNumTransactionPartitions]; -#endif - - // used to track conflicts due to kPolicyReadersOnly or WritersOnly - Transaction* _systemTransaction; - - // stats - LockStats _stats[kNumResourcePartitions]; - - // used when changing policy to/from Readers/Writers Only - AtomicUInt32 _numCurrentActiveReadRequests; - AtomicUInt32 _numCurrentActiveWriteRequests; - }; - - /** - * RAII wrapper around LockManager, for scoped locking - */ - class ResourceLock { - public: - ResourceLock(LockManager& lm, - Transaction* requestor, - const LockMode& mode, - const ResourceId& resId, - LockManager::Notifier* notifier = NULL); - - ~ResourceLock(); - private: - LockManager& _lm; - LockRequest _lr; - }; - - class SharedResourceLock : public ResourceLock { - public: - SharedResourceLock(Transaction* requestor, void* resource) - : ResourceLock(LockManager::getSingleton(), - requestor, - kShared, - (size_t)resource) { } - SharedResourceLock(Transaction* requestor, uint64_t resource) - : ResourceLock(LockManager::getSingleton(), - requestor, - kShared, - resource) { } - }; - - class ExclusiveResourceLock : public ResourceLock { - public: - ExclusiveResourceLock(Transaction* requestor, void* resource) - : ResourceLock(LockManager::getSingleton(), - requestor, - kExclusive, - (size_t)resource) { } - ExclusiveResourceLock(Transaction* requestor, uint64_t resource) - : ResourceLock(LockManager::getSingleton(), - requestor, - kExclusive, - resource) { } - }; -} // namespace mongo diff --git a/src/mongo/db/concurrency/lock_mgr_test.cpp b/src/mongo/db/concurrency/lock_mgr_test.cpp deleted file mode 100644 index 8dce49e94d1..00000000000 --- a/src/mongo/db/concurrency/lock_mgr_test.cpp +++ /dev/null @@ -1,894 +0,0 @@ -/** -* Copyright (C) 2014 MongoDB Inc. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License, version 3, -* as published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -* -* As a special exception, the copyright holders give permission to link the -* code of portions of this program with the OpenSSL library under certain -* conditions as described in each individual source file and distribute -* linked combinations including the program with the OpenSSL library. You -* must comply with the GNU Affero General Public License in all respects for -* all of the code used other than as permitted herein. If you modify file(s) -* with this exception, you may extend this exception to your version of the -* file(s), but you are not obligated to do so. If you do not wish to do so, -* delete this exception statement from your version. If you delete this -* exception statement from all source files in the program, then also delete -* it in the license file. -*/ - -/** - * tests for db/concurrency/lock_mgr.* capabilities - * - * Testing concurrency requires multiple threads. In this test, these are packaged as - * instances of a ClientTransaction class. The test's main thread creates instances - * of ClientTransactions and communicates with them through a pair of producer/consumer - * buffers. The driver thread sends requests to ClientTransaction threads using a - * TxCommandBuffer, and waits for responses in a TxResponseBuffer. This protocol - * allows precise control over timing. - * - * The producer/consumer buffer is possibly overkill. At present there is only - * one producer and one consumer using any one buffer. Also, the current set of - * tests are 'lock-step', with the driver never issuing more than one command - * before waiting for a response. - */ - -#include <boost/scoped_ptr.hpp> -#include <boost/thread/thread.hpp> -#include "mongo/unittest/unittest.h" -#include "mongo/db/concurrency/lock_mgr.h" -#include "mongo/util/log.h" -#include "mongo/util/time_support.h" - -namespace mongo { - - enum TxCmd { - ACQUIRE, - RELEASE, - ABORT, - POLICY, - QUIT, - INVALID - }; - - enum TxRsp { - ACQUIRED, - RELEASED, - BLOCKED, - AWAKENED, - ABORTED, - INVALID_RESPONSE - }; - - class TxResponse { - public: - TxRsp rspCode; - unsigned xid; - LockMode mode; - ResourceId resId; - }; - - class TxRequest { - public: - TxCmd cmd; - unsigned xid; - LockMode mode; - ResourceId resId; - LockManager::Policy policy; - }; - - class TxResponseBuffer { - public: - TxResponseBuffer() : _count(0), _readPos(0), _writePos(0) { } - - void post(const TxRsp& rspCode) { - boost::unique_lock<boost::mutex> guard(_guard); - while (_count == 10) - _full.wait(guard); - buffer[_writePos++].rspCode = rspCode; - _writePos %= 10; - _count++; - _empty.notify_one(); - } - - TxResponse* consume() { - boost::unique_lock<boost::mutex> guard(_guard); - while (_count == 0) - _empty.wait(guard); - TxResponse* result = &buffer[_readPos++]; - _readPos %= 10; - _count--; - _full.notify_one(); - return result; - } - - boost::mutex _guard; - boost::condition_variable _full; - boost::condition_variable _empty; - - size_t _count; - size_t _readPos; - size_t _writePos; - - TxResponse buffer[10]; - }; - -class TxCommandBuffer { -public: - TxCommandBuffer() : _count(0), _readPos(0), _writePos(0) { } - - void post(const TxCmd& cmd, - const unsigned& xid = 0, - const LockMode& mode = kShared, - const ResourceId& resId = 0, - const LockManager::Policy& policy = LockManager::kPolicyFirstCome) { - boost::unique_lock<boost::mutex> guard(_guard); - while (_count == 10) - _full.wait(guard); - buffer[_writePos].cmd = cmd; - buffer[_writePos].xid = xid; - buffer[_writePos].mode = mode; - buffer[_writePos].resId = resId; - buffer[_writePos].policy = policy; - _writePos++; - _writePos %= 10; - _count++; - _empty.notify_one(); - } - - TxRequest* consume() { - boost::unique_lock<boost::mutex> guard(_guard); - while (_count == 0) - _empty.wait(guard); - TxRequest* result = &buffer[_readPos++]; - _readPos %= 10; - _count--; - _full.notify_one(); - return result; - } - - boost::mutex _guard; - boost::condition_variable _full; - boost::condition_variable _empty; - - size_t _count; - size_t _readPos; - size_t _writePos; - - TxRequest buffer[10]; -}; - -class ClientTransaction : public LockManager::Notifier { -public: - // these are called in the main driver program - - ClientTransaction(LockManager* lm, const unsigned& xid) - : _lm(lm) - , _tx(xid) - , _thr(&ClientTransaction::processCmd, this) { } - - virtual ~ClientTransaction() { _thr.join(); } - - void acquire(const LockMode& mode, const ResourceId resId, const TxRsp& rspCode) { - _cmd.post(ACQUIRE, _tx.getTxId(), mode, resId); - TxResponse* rsp = _rsp.consume(); - ASSERT(rspCode == rsp->rspCode); - } - - void release(const LockMode& mode, const ResourceId resId) { - _cmd.post(RELEASE, _tx.getTxId(), mode, resId); - TxResponse* rsp = _rsp.consume(); - ASSERT(RELEASED == rsp->rspCode); - } - - void abort() { - _cmd.post(ABORT, _tx.getTxId()); - TxResponse* rsp = _rsp.consume(); - ASSERT(ABORTED == rsp->rspCode); - } - - void wakened() { - TxResponse* rsp = _rsp.consume(); - ASSERT(ACQUIRED == rsp->rspCode); - } - - void setPolicy(const LockManager::Policy& policy, const TxRsp& rspCode) { - _cmd.post(POLICY, _tx.getTxId(), kShared, 0, policy); - TxResponse* rsp = _rsp.consume(); - ASSERT(rspCode == rsp->rspCode); - } - - void quit() { - _cmd.post(QUIT); - } - - // these are run within the client threads - void processCmd() { - bool more = true; - while (more) { - TxRequest* req = _cmd.consume(); - switch (req->cmd) { - case ACQUIRE: - try { - _lm->acquire(&_tx, req->mode, req->resId, this); - _rsp.post(ACQUIRED); - } catch (const LockManager::AbortException& err) { - _rsp.post(ABORTED); -// log() << "t" << _tx._txId << ": aborted, ending" << endl; - return; - } - break; - case RELEASE: - _lm->release(&_tx, req->mode, req->resId); - _rsp.post(RELEASED); - break; - case ABORT: - try { - _lm->abort(&_tx); - } catch (const LockManager::AbortException& err) { - _rsp.post(ABORTED); - } - break; - case POLICY: - try { - _lm->setPolicy(&_tx, req->policy, this); - _rsp.post(ACQUIRED); - } catch( const LockManager::AbortException& err) { - _rsp.post(ABORTED); - } - break; - case QUIT: - default: - more = false; - break; - } - } - } - - // inherited from Notifier, used by LockManager::acquire - virtual void operator()(const Transaction* blocker) { - _rsp.post(BLOCKED); - } - -private: - TxCommandBuffer _cmd; - TxResponseBuffer _rsp; - LockManager* _lm; - Transaction _tx; - boost::thread _thr; - -}; - -TEST(LockManagerTest, TxError) { - LockManager lm; - LockManager::LockStatus status; - Transaction tx(1); - - // release a lock on a resource we haven't locked - lm.acquire(&tx, kShared, 2); - status = lm.release(&tx, kShared, 1); // this is in error - ASSERT(LockManager::kLockResourceNotFound == status); - status = lm.release(&tx, kShared, 2); - ASSERT(LockManager::kLockReleased == status); - - // release a record we've locked in a different mode - lm.acquire(&tx, kShared, 1); - status = lm.release(&tx, kExclusive, 1); // this is in error - ASSERT(LockManager::kLockModeNotFound == status); - status = lm.release(&tx, kShared, 1); - ASSERT(LockManager::kLockReleased == status); - - lm.acquire(&tx, kExclusive, 1); - status = lm.release(&tx, kShared, 1); // this is in error - ASSERT(LockManager::kLockModeNotFound == status); - status = lm.release(&tx, kExclusive, 1); - ASSERT(LockManager::kLockReleased == status); - - // attempt to acquire on a transaction that aborted - try { - lm.abort(&tx); - } catch (const LockManager::AbortException& err) { } - try { - lm.acquire(&tx, kShared, 1); // error - ASSERT(false); - } catch (const LockManager::AbortException& error) { - } -} - -TEST(LockManagerTest, SingleTx) { - LockManager lm; - Transaction t1(1); - ResourceId r1 = 1; - LockManager::LockStatus status; - - // acquire a shared record lock - ASSERT(! lm.isLocked(&t1, kShared, r1)); - lm.acquire(&t1, kShared, r1); - ASSERT(lm.isLocked(&t1, kShared, r1)); - - // release a shared record lock - lm.release(&t1, kShared, r1); - ASSERT(! lm.isLocked(&t1, kShared, r1)); - - // acquire a shared record lock twice, on same ResourceId - lm.acquire(&t1, kShared, r1); - lm.acquire(&t1, kShared, r1); - ASSERT(lm.isLocked(&t1, kShared, r1)); - - // release the twice-acquired lock, once. Still locked - status = lm.release(&t1, kShared, r1); - ASSERT(LockManager::kLockCountDecremented == status); - ASSERT(lm.isLocked(&t1, kShared, r1)); - - // after 2nd release, it's not locked - status = lm.release(&t1, kShared, r1); - ASSERT(LockManager::kLockReleased == status); - ASSERT(!lm.isLocked(&t1, kShared, r1)); - - - - // --- test downgrade and release --- - - // acquire an exclusive then a shared lock, on the same ResourceId - lm.acquire(&t1, kExclusive, r1); - ASSERT(lm.isLocked(&t1, kExclusive, r1)); - lm.acquire(&t1, kShared, r1); - ASSERT(lm.isLocked(&t1, kExclusive, r1)); - ASSERT(lm.isLocked(&t1, kShared, r1)); - - // release shared first, then exclusive - lm.release(&t1, kShared, r1); - ASSERT(! lm.isLocked(&t1, kShared, r1)); - ASSERT(lm.isLocked(&t1, kExclusive, r1)); - lm.release(&t1, kExclusive, r1); - ASSERT(! lm.isLocked(&t1, kExclusive, r1)); - - // release exclusive first, then shared - lm.acquire(&t1, kExclusive, r1); - lm.acquire(&t1, kShared, r1); - lm.release(&t1, kExclusive, r1); - ASSERT(! lm.isLocked(&t1, kExclusive, r1)); - ASSERT(lm.isLocked(&t1, kShared, r1)); - lm.release(&t1, kShared, r1); - ASSERT(! lm.isLocked(&t1, kShared, r1)); - - - - // --- test upgrade and release --- - - // acquire a shared, then an exclusive lock on the same ResourceId - lm.acquire(&t1, kShared, r1); - ASSERT(lm.isLocked(&t1, kShared, r1)); - lm.acquire(&t1, kExclusive, r1); - ASSERT(lm.isLocked(&t1, kShared, r1)); - ASSERT(lm.isLocked(&t1, kExclusive, r1)); - - // release exclusive first, then shared - lm.release(&t1, kExclusive, r1); - ASSERT(! lm.isLocked(&t1, kExclusive, r1)); - ASSERT(lm.isLocked(&t1, kShared, r1)); - lm.release(&t1, kShared, r1); - ASSERT(! lm.isLocked(&t1, kShared, r1)); - - // release shared first, then exclusive - lm.acquire(&t1, kShared, r1); - lm.acquire(&t1, kExclusive, r1); - lm.release(&t1, kShared, r1); - ASSERT(! lm.isLocked(&t1, kShared, r1)); - ASSERT(lm.isLocked(&t1, kExclusive, r1)); - lm.release(&t1, kExclusive, r1); - ASSERT(! lm.isLocked(&t1, kExclusive, r1)); -} - -TEST(LockManagerTest, TxConflict) { - LockManager lm; - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - - // no conflicts with shared locks on same/different objects - - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 2, ACQUIRED); - t1.acquire(kShared, 2, ACQUIRED); - t2.acquire(kShared, 1, ACQUIRED); - - t1.release(kShared, 1); - t1.release(kShared, 2); - t2.release(kShared, 1); - t2.release(kShared, 2); - - - // no conflicts with exclusive locks on different objects - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kExclusive, 2, ACQUIRED); - t1.release(kExclusive, 1); - t2.release(kExclusive, 2); - - - // shared then exclusive conflict - t1.acquire(kShared, 1, ACQUIRED); - // t2's request is incompatible with t1's lock, so it should block - t2.acquire(kExclusive, 1, BLOCKED); - t1.release(kShared, 1); - t2.wakened(); // with t1's lock released, t2 should wake - t2.release(kExclusive, 1); - - // exclusive then shared conflict - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - t2.wakened(); - t2.release(kShared, 1); - - // exclusive then exclusive conflict - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kExclusive, 1, BLOCKED); - t1.release(kExclusive, 1); - t2.wakened(); - t2.release(kExclusive, 1); - - t1.quit(); - t2.quit(); -} - -TEST(LockManagerTest, TxDeadlock) { - LockManager lm(LockManager::kPolicyReadersFirst); - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - - ClientTransaction a1(&lm, 4); - ClientTransaction a2(&lm, 5); - ClientTransaction a3(&lm, 6); - ClientTransaction a4(&lm, 7); - ClientTransaction a5(&lm, 8); - - // simple deadlock test 1 - t1.acquire(kShared, 1, ACQUIRED); - a1.acquire(kShared, 2, ACQUIRED); - t1.acquire(kExclusive, 2, BLOCKED); - // a1's request would form a dependency cycle, so it should abort - a1.acquire(kExclusive, 1, ABORTED); - t1.wakened(); // with a1's locks released, t1 should wake - t1.release(kExclusive, 2); - t1.release(kShared, 1); - - // simple deadlock test 2 - a2.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 2, ACQUIRED); - t2.acquire(kExclusive, 1, BLOCKED); - // a2's request would form a dependency cycle, so it should abort - a2.acquire(kExclusive, 2, ABORTED); - t2.wakened(); // with a2's locks released, t2 should wake - t2.release(kExclusive, 1); - t2.release(kShared, 2); - - // three way deadlock - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 2, ACQUIRED); - a3.acquire(kShared, 3, ACQUIRED); - t1.acquire(kExclusive, 2, BLOCKED); - t2.acquire(kExclusive, 3, BLOCKED); - // a3's request would form a dependency cycle, so it should abort - a3.acquire(kExclusive, 1, ABORTED); - t2.wakened(); // with a3's lock release, t2 should wake - t2.release(kShared, 2); - t1.wakened(); // with t2's locks released, t1 should wake - t2.release(kExclusive, 3); - t1.release(kShared, 1); - t1.release(kExclusive, 2); - - // test for phantom deadlocks - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kExclusive, 1, BLOCKED); - t1.release(kShared, 1); - t2.wakened(); - // at this point, t2 should no longer be waiting for t1 - // so it should be OK for t1 to wait for t2 - t1.acquire(kShared, 1, BLOCKED); - t2.release(kExclusive, 1); - t1.wakened(); - t1.release(kShared, 1); - - // test for missing deadlocks - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 2, ACQUIRED); // setup for deadlock with a4 - t2.acquire(kExclusive, 1, BLOCKED); // block on t1 - // after this, because readers first policy, t2 should - // also be waiting on a4. - a4.acquire(kShared, 1, ACQUIRED); - // after this, t2 should be waiting ONLY on a4 - t1.release(kShared, 1); - // So a4 should not be allowed to wait on t2's resource. - a4.acquire(kExclusive, 2, ABORTED); - t2.wakened(); - t2.release(kShared, 2); - t2.release(kExclusive, 1); - - // test for missing deadlocks: due to downgrades - a5.acquire(kExclusive, 1, ACQUIRED); - a5.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 2, ACQUIRED); // setup for deadlock with a5 - t2.acquire(kExclusive, 1, BLOCKED); // block on a5 - a5.release(kExclusive, 1); - // at this point, t2 should still be blocked on a5's downgraded lock - // So a5 should not be allowed to wait on t2's resource. - a5.acquire(kExclusive, 2, ABORTED); - t2.wakened(); - t2.release(kShared, 2); - t2.release(kExclusive, 1); - - t1.quit(); - t2.quit(); -} - -TEST(LockManagerTest, TxDowngrade) { - LockManager lm; - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - - t1.acquire(kExclusive, 1, ACQUIRED); - t1.acquire(kShared, 1, ACQUIRED); // downgrade - // t1 still has exclusive on resource 1, so t2 must wait - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - t2.wakened(); // with the exclusive lock released, t2 wakes - t1.release(kShared, 1); - t2.release(kShared, 1); - - t1.acquire(kExclusive, 1, ACQUIRED); - t1.acquire(kShared, 1, ACQUIRED); // downgrade - // t1 still has exclusive on resource 1, so t2 must wait - t2.acquire(kShared, 1, BLOCKED); - t1.release(kShared, 1); - // with t1 still holding exclusive on resource 1, t2 still blocked - t1.release(kExclusive, 1); - t2.wakened(); // with the exclusive lock released, t2 wakes - t2.release(kShared, 1); - - t1.acquire(kExclusive, 1, ACQUIRED); - // t1 has exclusive on resource 1, so t2 must wait - t2.acquire(kShared, 1, BLOCKED); - // even though t2 is waiting for resource 1, t1 can still use it shared, - // because it already owns exclusive lock and can't block on itself - t1.acquire(kShared, 1, ACQUIRED); - t1.release(kExclusive, 1); - t2.wakened(); // with the exclusive lock released, t2 wakes - t1.release(kShared, 1); - t2.release(kShared, 1); - - // t2 acquires exclusive during t1's downgrade - t1.acquire(kExclusive, 1, ACQUIRED); - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kExclusive, 1, BLOCKED); - t1.release(kExclusive, 1); - t1.release(kShared, 1); - t2.wakened(); - t2.release(kExclusive, 1); - - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kExclusive, 1, BLOCKED); - t1.acquire(kShared, 1, ACQUIRED); - t1.release(kExclusive, 1); - t1.release(kShared, 1); - t2.wakened(); - t2.release(kExclusive, 1); - - t1.quit(); - t2.quit(); -} - -TEST(LockManagerTest, TxUpgrade) { - LockManager lm(LockManager::kPolicyReadersFirst); - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - ClientTransaction t3(&lm, 3); - - ClientTransaction a2(&lm, 4); - ClientTransaction a3(&lm, 5); - - // test upgrade succeeds, blocks subsequent reads - t1.acquire(kShared, 1, ACQUIRED); - t1.acquire(kExclusive, 1, ACQUIRED); // upgrade - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - t2.wakened(); - t1.release(kShared, 1); - t2.release(kShared, 1); - - // test upgrade blocks, then wakes - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 1, ACQUIRED); - // t1 can't use resource 1 exclusively yet, because t2 is using it - t1.acquire(kExclusive, 1, BLOCKED); - t2.release(kShared, 1); - t1.wakened(); // with t2's shared lock released, t1 wakes - t1.release(kExclusive, 1); - t1.release(kShared, 1); - - // test upgrade blocks on several, then wakes - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 1, ACQUIRED); - // t1 can't use resource 1 exclusively yet, because t2 is using it - t1.acquire(kExclusive, 1, BLOCKED); - t3.acquire(kShared, 1, ACQUIRED); // additional blocker - t2.release(kShared, 1); // t1 still blocked - t3.release(kShared, 1); - t1.wakened(); // with t3's shared lock released, t1 wakes - t1.release(kExclusive, 1); - t1.release(kShared, 1); - - // failure to upgrade - t1.acquire(kShared, 1, ACQUIRED); - a2.acquire(kShared, 1, ACQUIRED); - t1.acquire(kExclusive, 1, BLOCKED); - a2.acquire(kExclusive, 1, ABORTED); - // with a2's abort, t1 can wake - t1.wakened(); - t1.release(kShared, 1); - t1.release(kExclusive, 1); - - // failure to upgrade - t1.acquire(kShared, 1, ACQUIRED); - t2.acquire(kShared, 1, ACQUIRED); - t1.acquire(kExclusive, 1, BLOCKED); - a3.acquire(kShared, 1, ACQUIRED); - t2.release(kShared, 1); // t1 still blocked on a3 - a3.acquire(kExclusive, 1, ABORTED); - - t1.quit(); - t2.quit(); - t3.quit(); -} -#if 0 -TEST(LockManagerTest, TxPolicy) { - - { - // Test FirstComeFirstServe policy - LockManager lm_first; - ClientTransaction t1(&lm_first, 1); - ClientTransaction t2(&lm_first, 2); - ClientTransaction t3(&lm_first, 3); - // test1 - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kShared, 1, BLOCKED); - t3.acquire(kExclusive, 1, BLOCKED); - t1.release(kExclusive, 1); - // t2 should wake first, because its request came before t3's - t2.wakened(); - t2.release(kShared, 1); - t3.wakened(); - t3.release(kExclusive, 1); - - // test2 - t1.acquire(kExclusive, 1, ACQUIRED); - t3.acquire(kExclusive, 1, BLOCKED); - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - // t3 should wake first, because its request came before t2's - t3.wakened(); - t3.release(kExclusive, 1); - t2.wakened(); - t2.release(kShared, 1); - - t1.quit(); - t2.quit(); - t3.quit(); - } - - { - // Test kPolicyReadersFirst - // shared request are considered read requests - - LockManager lm_readers(LockManager::kPolicyReadersFirst); - ClientTransaction t1(&lm_readers, 1); - ClientTransaction t2(&lm_readers, 2); - ClientTransaction t3(&lm_readers, 3); - - t1.acquire(kExclusive, 1, ACQUIRED); - t3.acquire(kExclusive, 1, BLOCKED); - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - - // t2 should wake first, even though t3 came first in time - // because t2 is a reader and t3 is a writer - t2.wakened(); - t2.release(kShared, 1); - t3.wakened(); - t3.release(kExclusive, 1); - - t1.quit(); - t2.quit(); - t3.quit(); - } - - { - // Test OLDEST_TX_FIRST policy - // for now, smaller TxIds are considered older - - LockManager lm_oldest(LockManager::kPolicyOldestTxFirst); - ClientTransaction t1(&lm_oldest, 1); - ClientTransaction t2(&lm_oldest, 2); - ClientTransaction t3(&lm_oldest, 3); - - // test 1 - t1.acquire(kExclusive, 1, ACQUIRED); - t3.acquire(kExclusive, 1, BLOCKED); - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - - // t2 should wake first, even though t3 came first in time - // because t2 is older than t3 - t2.wakened(); - t2.release(kShared, 1); - t3.wakened(); - t3.release(kExclusive, 1); - - // test 2 - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kShared, 1, BLOCKED); - t3.acquire(kExclusive, 1, BLOCKED); - t1.release(kExclusive, 1); - - // t2 should wake first, because it's older than t3 - t2.wakened(); - t2.release(kShared, 1); - t3.wakened(); - t3.release(kExclusive, 1); - - t1.quit(); - t2.quit(); - t3.quit(); - } - - { - LockManager lm_blockers(LockManager::kPolicyBlockersFirst); - ClientTransaction t1(&lm_blockers, 1); - ClientTransaction t2(&lm_blockers, 2); - ClientTransaction t3(&lm_blockers, 3); - ClientTransaction t4(&lm_blockers, 4); - - // BIGGEST_BLOCKER_FIRST policy - - // set up t3 as the biggest blocker - t3.acquire(kExclusive, 2, ACQUIRED); - t4.acquire(kExclusive, 2, BLOCKED); - - // test 1 - t1.acquire(kExclusive, 1, ACQUIRED); - t3.acquire(kExclusive, 1, BLOCKED); - t2.acquire(kShared, 1, BLOCKED); - t1.release(kExclusive, 1); - // t3 should wake first, because it's a bigger blocker than t2 - t3.wakened(); - t3.release(kExclusive, 1); - t2.wakened(); - t2.release(kShared, 1); - - // test 2 - t1.acquire(kExclusive, 1, ACQUIRED); - t2.acquire(kShared, 1, BLOCKED); - t3.acquire(kExclusive, 1, BLOCKED); - t1.release(kExclusive, 1); - // t3 should wake first, even though t2 came first, - // because it's a bigger blocker than t2 - t3.wakened(); - t3.release(kExclusive, 1); - t2.wakened(); - t2.release(kShared, 1); - - t3.release(kExclusive, 2); - t4.wakened(); - t4.release(kExclusive, 2); - - t1.quit(); - t2.quit(); - t3.quit(); - t4.quit(); - } -} - -/* - * test kPolicyReadersOnly and kPolicyWritersOnly - */ -TEST(LockManagerTest, TxOnlyPolicies) { - LockManager lm; - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - ClientTransaction t3(&lm, 3); - ClientTransaction t4(&lm, 4); - ClientTransaction t5(&lm, 5); - ClientTransaction tp(&lm, 6); - - // show kPolicyReadersOnly blocking writers, which - // awake when policy reverts - t1.acquire(kShared, 1, ACQUIRED); - tp.setPolicy(LockManager::kPolicyReadersOnly, ACQUIRED); - t3.acquire(kExclusive, 2, BLOCKED); // just policy conflict - t4.acquire(kExclusive, 1, BLOCKED); // both policy & t1 - t5.acquire(kShared, 1, ACQUIRED); // even tho t4 - tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED); - t3.wakened(); - t3.release(kExclusive, 2); - t1.release(kShared, 1); - t5.release(kShared, 1); - t4.wakened(); - t4.release(kExclusive, 1); - - // show WRITERS_ONLY blocking readers, which - // awake when policy reverts - t1.acquire(kExclusive, 1, ACQUIRED); - tp.setPolicy(LockManager::kPolicyWritersOnly, ACQUIRED); - t3.acquire(kShared, 2, BLOCKED); // just policy conflict - t4.acquire(kShared, 1, BLOCKED); // both policy & t1 - t1.release(kExclusive, 1); - t5.acquire(kExclusive, 2, ACQUIRED); // even tho t3 - t5.release(kExclusive, 2); - tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED); - t3.wakened(); - t3.release(kShared, 2); - t4.wakened(); - t4.release(kShared, 1); - - // show READERS_ONLY blocked by existing writer - // but still blocking new writers - t1.acquire(kExclusive, 1, ACQUIRED); - tp.setPolicy(LockManager::kPolicyReadersOnly, BLOCKED); // blocked by t1 - t2.acquire(kExclusive, 2, BLOCKED); // just policy conflict - t3.acquire(kShared, 2, ACQUIRED); // even tho t2 - t3.release(kShared, 2); - t1.release(kExclusive, 1); - tp.wakened(); - tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED); - t2.wakened(); - t2.release(kExclusive, 2); - - // show WRITERS_ONLY blocked by existing reader - // but still blocking new readers - t1.acquire(kShared, 1, ACQUIRED); - tp.setPolicy(LockManager::kPolicyWritersOnly, BLOCKED); // blocked by t1 - t2.acquire(kShared, 2, BLOCKED); // just policy conflict - t1.release(kShared, 1); - tp.wakened(); - tp.setPolicy(LockManager::kPolicyReadersFirst, ACQUIRED); - t2.wakened(); - t2.release(kShared, 2); - - t1.quit(); - t2.quit(); - t3.quit(); - t4.quit(); - t5.quit(); - tp.quit(); -} -#endif -TEST(LockManagerTest, TxShutdown) { - LockManager lm; - ClientTransaction t1(&lm, 1); - ClientTransaction t2(&lm, 2); - - t1.acquire(kShared, 1, ACQUIRED); - lm.shutdown(3000); - - // t1 can still do work while quiescing - t1.release(kShared, 1); - t1.acquire(kShared, 2, ACQUIRED); -#ifdef TRANSACTION_REGISTRATION - // t2 is new and should be refused - t2.acquire(kShared, 3, ABORTED); -#else - t2.quit(); -#endif - // after the quiescing period, t1's request should be refused - sleepsecs(3); - t1.acquire(kShared, 4, ABORTED); -} -} diff --git a/src/mongo/db/exec/update.cpp b/src/mongo/db/exec/update.cpp index 74a3d008adf..aa5a895df70 100644 --- a/src/mongo/db/exec/update.cpp +++ b/src/mongo/db/exec/update.cpp @@ -515,10 +515,6 @@ namespace mongo { else { // The updates were not in place. Apply them through the file manager. - // XXX: With experimental document-level locking, we do not hold - // sufficient locks, so this would cause corruption. - fassert(18516, !useExperimentalDocLocking); - newObj = _doc.getObject(); uassert(17419, str::stream() << "Resulting document after update is larger than " diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index e4efd8bc6b3..d46b08ce0e8 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -35,7 +35,6 @@ #include "mongo/base/string_data.h" #include "mongo/db/storage/recovery_unit.h" #include "mongo/db/concurrency/locker.h" -#include "mongo/db/concurrency/lock_mgr.h" namespace mongo { @@ -142,11 +141,6 @@ namespace mongo { */ virtual bool isPrimaryFor( const StringData& ns ) = 0; - /** - * @return Transaction* for LockManager-ment. Caller does not own pointer - */ - virtual Transaction* getTransaction() = 0; - protected: OperationContext() { } }; diff --git a/src/mongo/db/operation_context_impl.cpp b/src/mongo/db/operation_context_impl.cpp index 596d9d4a1a7..a7f66d5452a 100644 --- a/src/mongo/db/operation_context_impl.cpp +++ b/src/mongo/db/operation_context_impl.cpp @@ -207,8 +207,4 @@ namespace mongo { NamespaceString(ns).db()); } - Transaction* OperationContextImpl::getTransaction() { - return _tx.setTxIdOnce((unsigned)getCurOp()->opNum()); - } - } // namespace mongo diff --git a/src/mongo/db/operation_context_impl.h b/src/mongo/db/operation_context_impl.h index e9c43c08928..b8ae53a2185 100644 --- a/src/mongo/db/operation_context_impl.h +++ b/src/mongo/db/operation_context_impl.h @@ -70,14 +70,9 @@ namespace mongo { virtual bool isPrimaryFor( const StringData& ns ); - virtual Transaction* getTransaction(); - private: std::auto_ptr<RecoveryUnit> _recovery; - - Transaction _tx; - - boost::scoped_ptr<Locker> _locker; + std::auto_ptr<Locker> _locker; }; } // namespace mongo diff --git a/src/mongo/db/operation_context_noop.h b/src/mongo/db/operation_context_noop.h index ea991a855ad..d2ac6d47d77 100644 --- a/src/mongo/db/operation_context_noop.h +++ b/src/mongo/db/operation_context_noop.h @@ -104,10 +104,6 @@ namespace mongo { return 0; } - virtual Transaction* getTransaction() { - return NULL; - } - private: std::auto_ptr<RecoveryUnit> _recoveryUnit; }; diff --git a/src/mongo/db/startup_warnings_mongod.cpp b/src/mongo/db/startup_warnings_mongod.cpp index 0e88708737f..c46787bc122 100644 --- a/src/mongo/db/startup_warnings_mongod.cpp +++ b/src/mongo/db/startup_warnings_mongod.cpp @@ -41,8 +41,6 @@ namespace mongo { - extern bool useExperimentalDocLocking; - void logMongodStartupWarnings() { logCommonStartupWarnings(); @@ -61,18 +59,6 @@ namespace mongo { warned = true; } - if (useExperimentalDocLocking) { - log() << "** WARNING: Experimental (and untested) document-level locking for in-place" - << startupWarningsLog; - log() << " updates, which do not modify indexed values, has been enabled." - << startupWarningsLog; - log() << " This should absolutely not be used on production systems and is" - << startupWarningsLog; - log() << " for demonstration purposes only." - << startupWarningsLog; - warned = true; - } - if (!ProcessInfo::blockCheckSupported()) { log() << startupWarningsLog; log() << "** NOTE: your operating system version does not support the method that " diff --git a/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp b/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp index fa193294aae..4ad0cf04969 100644 --- a/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp +++ b/src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp @@ -31,7 +31,6 @@ #include "mongo/db/storage/mmap_v1/record_store_v1_base.h" #include "mongo/db/catalog/collection.h" -#include "mongo/db/concurrency/lock_mgr.h" #include "mongo/db/operation_context.h" #include "mongo/db/storage/mmap_v1/extent.h" #include "mongo/db/storage/mmap_v1/extent_manager.h" |