summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-10-06 17:40:01 -0400
committerKaloian Manassiev <kaloian.manassiev@mongodb.com>2014-10-08 17:38:06 -0400
commit738c83f6671178e663cef0c3f548f5a046039cc9 (patch)
tree92bba8ccae04feb59e61bd52f2be1c8da380962a /src/mongo
parentb8b905ba5369df65a752b47408327367737d4bde (diff)
downloadmongo-738c83f6671178e663cef0c3f548f5a046039cc9.tar.gz
SERVER-14668 Remove 'experimental doc locking legacy code'
o Cleans up references to the experimental doc locking code. o Removes the 'old' lock manager. o Moves the 'new' lock manager out of the 'newlm' namespace.
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/catalog/collection.cpp2
-rw-r--r--src/mongo/db/catalog/collection_cursor_cache.cpp14
-rw-r--r--src/mongo/db/concurrency/SConscript1
-rw-r--r--src/mongo/db/concurrency/lock_mgr.cpp1334
-rw-r--r--src/mongo/db/concurrency/lock_mgr.h741
-rw-r--r--src/mongo/db/concurrency/lock_mgr_test.cpp894
-rw-r--r--src/mongo/db/exec/update.cpp4
-rw-r--r--src/mongo/db/operation_context.h6
-rw-r--r--src/mongo/db/operation_context_impl.cpp4
-rw-r--r--src/mongo/db/operation_context_impl.h7
-rw-r--r--src/mongo/db/operation_context_noop.h4
-rw-r--r--src/mongo/db/startup_warnings_mongod.cpp14
-rw-r--r--src/mongo/db/storage/mmap_v1/record_store_v1_base.cpp1
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"