From c610cfe5c58d1f4301f5535d3e166d5d4332bc87 Mon Sep 17 00:00:00 2001 From: Kaloian Manassiev Date: Wed, 13 Aug 2014 12:18:53 -0400 Subject: SERVER-14668 Move locking related files to be under mongo/db/concurrency Also performs some cleanup of the includes around locking. --- src/mongo/SConscript | 3 - .../db/auth/authz_session_external_state_d.cpp | 2 +- src/mongo/db/catalog/collection_info_cache.cpp | 2 +- src/mongo/db/catalog/database_holder.cpp | 2 +- src/mongo/db/catalog/database_holder.h | 2 +- src/mongo/db/client.h | 2 +- src/mongo/db/commands/compact.cpp | 2 +- src/mongo/db/commands/fsync.cpp | 2 +- src/mongo/db/commands/touch.cpp | 2 +- src/mongo/db/concurrency/SConscript | 3 + src/mongo/db/concurrency/d_concurrency.cpp | 826 ++++++++++++++++++++ src/mongo/db/concurrency/d_concurrency.h | 253 +++++++ src/mongo/db/concurrency/lock_stat.cpp | 127 ++++ src/mongo/db/concurrency/lock_stat.h | 68 ++ src/mongo/db/concurrency/lock_state.cpp | 311 ++++++++ src/mongo/db/concurrency/lock_state.h | 178 +++++ src/mongo/db/d_concurrency.cpp | 831 --------------------- src/mongo/db/d_concurrency.h | 253 ------- src/mongo/db/db.cpp | 2 +- src/mongo/db/index_builder.cpp | 2 +- src/mongo/db/instance.cpp | 2 +- src/mongo/db/lockstat.cpp | 127 ---- src/mongo/db/lockstat.h | 68 -- src/mongo/db/lockstate.cpp | 311 -------- src/mongo/db/lockstate.h | 177 ----- src/mongo/db/operation_context.h | 2 +- src/mongo/db/repl/network_interface_impl.cpp | 2 +- src/mongo/db/repl/rs_sync.cpp | 2 +- src/mongo/db/stats/snapshots_webplugins.cpp | 2 +- .../db/storage/mmap_v1/catalog/namespace_index.cpp | 2 +- src/mongo/db/storage/mmap_v1/data_file.cpp | 5 +- src/mongo/db/storage/mmap_v1/dur_commitjob.h | 2 +- .../db/storage/mmap_v1/durable_mapped_file.cpp | 2 +- src/mongo/db/storage/mmap_v1/durop.cpp | 2 +- .../db/storage/mmap_v1/mmap_v1_extent_manager.cpp | 2 +- src/mongo/dbtests/commandtests.cpp | 2 +- src/mongo/dbtests/threadedtests.cpp | 2 +- src/mongo/s/d_merge.cpp | 2 +- src/mongo/s/s_only.cpp | 2 - src/mongo/util/mmap_posix.cpp | 2 +- src/mongo/util/mmap_win.cpp | 2 +- 41 files changed, 1793 insertions(+), 1800 deletions(-) create mode 100644 src/mongo/db/concurrency/d_concurrency.cpp create mode 100644 src/mongo/db/concurrency/d_concurrency.h create mode 100644 src/mongo/db/concurrency/lock_stat.cpp create mode 100644 src/mongo/db/concurrency/lock_stat.h create mode 100644 src/mongo/db/concurrency/lock_state.cpp create mode 100644 src/mongo/db/concurrency/lock_state.h delete mode 100644 src/mongo/db/d_concurrency.cpp delete mode 100644 src/mongo/db/d_concurrency.h delete mode 100644 src/mongo/db/lockstat.cpp delete mode 100644 src/mongo/db/lockstat.h delete mode 100644 src/mongo/db/lockstate.cpp delete mode 100644 src/mongo/db/lockstate.h diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 036a96c7d08..c3945e2b154 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -562,9 +562,6 @@ serverOnlyFiles = [ "db/curop.cpp", "db/d_globals.cpp", "util/compress.cpp", "db/ttl.cpp", - "db/d_concurrency.cpp", - "db/lockstat.cpp", - "db/lockstate.cpp", "util/logfile.cpp", "util/alignedbuilder.cpp", "util/elapsed_tracker.cpp", diff --git a/src/mongo/db/auth/authz_session_external_state_d.cpp b/src/mongo/db/auth/authz_session_external_state_d.cpp index a24f11bedab..c7d289f7fc2 100644 --- a/src/mongo/db/auth/authz_session_external_state_d.cpp +++ b/src/mongo/db/auth/authz_session_external_state_d.cpp @@ -33,7 +33,7 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/client.h" #include "mongo/db/dbhelpers.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/instance.h" #include "mongo/db/jsobj.h" #include "mongo/scripting/engine.h" diff --git a/src/mongo/db/catalog/collection_info_cache.cpp b/src/mongo/db/catalog/collection_info_cache.cpp index 63bf44e80db..f0482cad178 100644 --- a/src/mongo/db/catalog/collection_info_cache.cpp +++ b/src/mongo/db/catalog/collection_info_cache.cpp @@ -35,7 +35,7 @@ #include "mongo/db/catalog/collection_info_cache.h" #include "mongo/db/catalog/collection.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/fts/fts_spec.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index_legacy.h" diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp index 12ee303a6e8..fa12613c662 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -38,7 +38,7 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/catalog/database_catalog_entry.h" #include "mongo/db/catalog/database_holder.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/global_environment_experiment.h" #include "mongo/db/operation_context_impl.h" #include "mongo/db/storage/mmap_v1/dur.h" diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h index bd3b3cd0133..1d067af7c00 100644 --- a/src/mongo/db/catalog/database_holder.h +++ b/src/mongo/db/catalog/database_holder.h @@ -32,7 +32,7 @@ #include "mongo/base/string_data.h" #include "mongo/db/catalog/database.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/namespace_string.h" #include "mongo/util/string_map.h" diff --git a/src/mongo/db/client.h b/src/mongo/db/client.h index 65b72b09dc1..72c290af3c6 100644 --- a/src/mongo/db/client.h +++ b/src/mongo/db/client.h @@ -37,7 +37,7 @@ #pragma once #include "mongo/db/client_basic.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/lasterror.h" #include "mongo/db/repl/rs.h" #include "mongo/db/stats/top.h" diff --git a/src/mongo/db/commands/compact.cpp b/src/mongo/db/commands/compact.cpp index a31b84cf1b9..0ec15bcc8c3 100644 --- a/src/mongo/db/commands/compact.cpp +++ b/src/mongo/db/commands/compact.cpp @@ -39,7 +39,7 @@ #include "mongo/db/catalog/database.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/index_builder.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context_impl.h" diff --git a/src/mongo/db/commands/fsync.cpp b/src/mongo/db/commands/fsync.cpp index df304429935..8822269a43f 100644 --- a/src/mongo/db/commands/fsync.cpp +++ b/src/mongo/db/commands/fsync.cpp @@ -39,7 +39,7 @@ #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/commands.h" #include "mongo/db/global_environment_experiment.h" #include "mongo/db/storage/mmap_v1/dur.h" diff --git a/src/mongo/db/commands/touch.cpp b/src/mongo/db/commands/touch.cpp index e5ca59c652a..187ba27ab04 100644 --- a/src/mongo/db/commands/touch.cpp +++ b/src/mongo/db/commands/touch.cpp @@ -43,7 +43,7 @@ #include "mongo/db/catalog/database.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/jsobj.h" #include "mongo/db/operation_context_impl.h" #include "mongo/util/timer.h" diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript index 97507d58197..1dd9355c81f 100644 --- a/src/mongo/db/concurrency/SConscript +++ b/src/mongo/db/concurrency/SConscript @@ -6,6 +6,9 @@ env.Library( target='lock_mgr', source=[ 'lock_mgr.cpp', + 'd_concurrency.cpp', + 'lock_stat.cpp', + 'lock_state.cpp' ], LIBDEPS=[ '$BUILD_DIR/mongo/base/base', diff --git a/src/mongo/db/concurrency/d_concurrency.cpp b/src/mongo/db/concurrency/d_concurrency.cpp new file mode 100644 index 00000000000..f331af72ec5 --- /dev/null +++ b/src/mongo/db/concurrency/d_concurrency.cpp @@ -0,0 +1,826 @@ +// @file d_concurrency.cpp + +/** +* Copyright (C) 2008-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 . +* +* 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/d_concurrency.h" + +#include "mongo/db/commands/server_status.h" +#include "mongo/db/curop.h" +#include "mongo/db/d_globals.h" +#include "mongo/db/global_environment_experiment.h" +#include "mongo/db/concurrency/lock_stat.h" +#include "mongo/db/server_parameters.h" +#include "mongo/db/operation_context.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/concurrency/mapsf.h" +#include "mongo/util/concurrency/qlock.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" +#include "mongo/util/stacktrace.h" + +// oplog locking +// no top level read locks +// system.profile writing +// oplog now +// yielding + +namespace mongo { + + class DBTryLockTimeoutException : public std::exception { + public: + DBTryLockTimeoutException() {} + virtual ~DBTryLockTimeoutException() throw() { } + }; + + /* dbname->lock + Currently these are never deleted - will linger if db was closed. (that should be fine.) + We don't put the lock inside the Database object as those can come and go with open and + closes and that would just add complexity. + Note there is no path concept for where the database is; if somehow you had two db's open + in different directories with the same name, it will be ok but they are sharing a lock + then. + */ + typedef mapsf< StringMap > DBLocksMap; + static DBLocksMap dblocks; + + /* we don't want to touch dblocks too much as a mutex is involved. thus party for that, + this is here... + */ + WrapperForRWLock *nestableLocks[] = { + 0, + new WrapperForRWLock("local"), + new WrapperForRWLock("admin") + }; + + LockStat* Lock::nestableLockStat( Nestable db ) { + return &nestableLocks[db]->getStats(); + } + + class WrapperForQLock { + public: + QLock q; + LockStat stats; + + void lock_R(LockState* lockState) { + invariant(lockState->threadState() == 0); + lockState->lockedStart('R'); + q.lock_R(); + } + + void lock_W(LockState* lockState) { + if (lockState->threadState()) { + log() << "can't lock_W, threadState=" << (int)lockState->threadState() << endl; + fassert(16114,false); + } + + lockState->lockedStart('W'); + q.lock_W(); + } + + // how to count try's that fail is an interesting question. we should get rid of try(). + bool lock_R_try(LockState* lockState, int millis) { + verify(lockState->threadState() == 0); + bool got = q.lock_R_try(millis); + if (got) { + lockState->lockedStart('R'); + } + return got; + } + + bool lock_W_try(LockState* lockState, int millis) { + verify(lockState->threadState() == 0); + bool got = q.lock_W_try(millis); + if( got ) { + lockState->lockedStart('W'); + } + return got; + } + + void unlock_R(LockState* lockState) { + wassert(lockState->threadState() == 'R'); + lockState->unlocked(); + q.unlock_R(); + } + + void unlock_W(LockState* lockState) { + wassert(lockState->threadState() == 'W'); + lockState->unlocked(); + q.unlock_W(); + } + + // todo timing stats? : + void W_to_R() { q.W_to_R(); } + void R_to_W() { q.R_to_W(); } + bool w_to_X() { return q.w_to_X(); } + void X_to_w() { q.X_to_w(); } + }; + + static WrapperForQLock& qlk = *new WrapperForQLock(); + LockStat* Lock::globalLockStat() { + return &qlk.stats; + } + + + RWLockRecursive &Lock::ParallelBatchWriterMode::_batchLock = *(new RWLockRecursive("special")); + void Lock::ParallelBatchWriterMode::iAmABatchParticipant(LockState* lockState) { + lockState->_batchWriter = true; + } + + Lock::ScopedLock::ParallelBatchWriterSupport::ParallelBatchWriterSupport(LockState* lockState) + : _lockState(lockState) { + relock(); + } + + void Lock::ScopedLock::ParallelBatchWriterSupport::tempRelease() { + _lk.reset( 0 ); + } + + void Lock::ScopedLock::ParallelBatchWriterSupport::relock() { + if (!_lockState->_batchWriter) { + AcquiringParallelWriter a(*_lockState); + _lk.reset( new RWLockRecursive::Shared(ParallelBatchWriterMode::_batchLock) ); + } + } + + + Lock::ScopedLock::ScopedLock(LockState* lockState, char type) + : _lockState(lockState), _pbws_lk(lockState), _type(type), _stat(0) { + + _lockState->enterScopedLock(this); + } + + Lock::ScopedLock::~ScopedLock() { + int prevCount = _lockState->recursiveCount(); + Lock::ScopedLock* what = _lockState->leaveScopedLock(); + fassert( 16171 , prevCount != 1 || what == this ); + } + + long long Lock::ScopedLock::acquireFinished( LockStat* stat ) { + long long acquisitionTime = _timer.micros(); + _timer.reset(); + _stat = stat; + + // increment the operation level statistics + cc().curop()->lockStat().recordAcquireTimeMicros( _type , acquisitionTime ); + + return acquisitionTime; + } + + void Lock::ScopedLock::tempRelease() { + long long micros = _timer.micros(); + _tempRelease(); + _pbws_lk.tempRelease(); + _recordTime( micros ); // might as well do after we unlock + } + + void Lock::ScopedLock::_recordTime( long long micros ) { + if ( _stat ) + _stat->recordLockTimeMicros( _type , micros ); + cc().curop()->lockStat().recordLockTimeMicros( _type , micros ); + } + + void Lock::ScopedLock::recordTime() { + _recordTime(_timer.micros()); + } + + void Lock::ScopedLock::resetTime() { + _timer.reset(); + } + + void Lock::ScopedLock::relock() { + _pbws_lk.relock(); + resetTime(); + _relock(); + } + + Lock::TempRelease::TempRelease(LockState* lockState) + : cant(lockState->isRecursive()), _lockState(lockState) { + + if( cant ) + return; + + fassert(16116, _lockState->recursiveCount() == 1); + fassert(16117, _lockState->threadState() != 0); + + scopedLk = _lockState->leaveScopedLock(); + fassert(16118, scopedLk); + + invariant(_lockState == scopedLk->_lockState); + + scopedLk->tempRelease(); + } + Lock::TempRelease::~TempRelease() + { + if( cant ) + return; + + fassert(16119, scopedLk); + fassert(16120, _lockState->threadState() == 0); + + _lockState->enterScopedLock(scopedLk); + scopedLk->relock(); + } + + void Lock::GlobalWrite::_tempRelease() { + fassert(16121, !noop); + char ts = _lockState->threadState(); + fassert(16122, ts != 'R'); // indicates downgraded; not allowed with temprelease + fassert(16123, ts == 'W'); + qlk.unlock_W(_lockState); + } + void Lock::GlobalWrite::_relock() { + fassert(16125, !noop); + char ts = _lockState->threadState(); + fassert(16126, ts == 0); + Acquiring a(this, *_lockState); + qlk.lock_W(_lockState); + } + + void Lock::GlobalRead::_tempRelease() { + fassert(16127, !noop); + char ts = _lockState->threadState(); + fassert(16128, ts == 'R'); + qlk.unlock_R(_lockState); + } + void Lock::GlobalRead::_relock() { + fassert(16129, !noop); + char ts = _lockState->threadState(); + fassert(16130, ts == 0); + Acquiring a(this, *_lockState); + qlk.lock_R(_lockState); + } + + void Lock::DBWrite::_tempRelease() { + unlockDB(); + } + void Lock::DBWrite::_relock() { + lockDB(_what); + } + void Lock::DBRead::_tempRelease() { + unlockDB(); + } + void Lock::DBRead::_relock() { + lockDB(_what); + } + + Lock::GlobalWrite::GlobalWrite(LockState* lockState, int timeoutms) + : ScopedLock(lockState, 'W') { + + char ts = _lockState->threadState(); + noop = false; + if( ts == 'W' ) { + noop = true; + return; + } + dassert( ts == 0 ); + + Acquiring a(this, *_lockState); + + if ( timeoutms != -1 ) { + bool success = qlk.lock_W_try(_lockState, timeoutms); + if ( !success ) throw DBTryLockTimeoutException(); + } + else { + qlk.lock_W(_lockState); + } + } + Lock::GlobalWrite::~GlobalWrite() { + if( noop ) { + return; + } + recordTime(); // for lock stats + if (_lockState->threadState() == 'R') { // we downgraded + qlk.unlock_R(_lockState); + } + else { + qlk.unlock_W(_lockState); + } + } + void Lock::GlobalWrite::downgrade() { + verify( !noop ); + verify(_lockState->threadState() == 'W'); + + qlk.W_to_R(); + _lockState->changeLockState('R'); + } + + // you will deadlock if 2 threads doing this + void Lock::GlobalWrite::upgrade() { + verify( !noop ); + verify(_lockState->threadState() == 'R'); + + qlk.R_to_W(); + _lockState->changeLockState('W'); + } + + Lock::GlobalRead::GlobalRead(LockState* lockState, int timeoutms) + : ScopedLock(lockState, 'R') { + + char ts = _lockState->threadState(); + noop = false; + if( ts == 'R' || ts == 'W' ) { + noop = true; + return; + } + + Acquiring a(this, *_lockState); + + if ( timeoutms != -1 ) { + bool success = qlk.lock_R_try(_lockState, timeoutms); + if ( !success ) throw DBTryLockTimeoutException(); + } + else { + // we are unlocked in the qlock/top sense. lock_R will assert if we are in an in compatible state + qlk.lock_R(_lockState); + } + } + Lock::GlobalRead::~GlobalRead() { + if( !noop ) { + recordTime(); // for lock stats + qlk.unlock_R(_lockState); + } + } + + void Lock::DBWrite::lockNestable(Nestable db) { + _nested = true; + + if (_lockState->nestableCount()) { + if( db != _lockState->whichNestable() ) { + error() << "can't lock local and admin db at the same time " << (int) db << ' ' << (int) _lockState->whichNestable() << endl; + fassert(16131,false); + } + verify( _lockState->nestableCount() > 0 ); + } + else { + fassert(16132,_weLocked==0); + _lockState->lockedNestable(db, 1); + _weLocked = nestableLocks[db]; + _weLocked->lock(); + } + } + void Lock::DBRead::lockNestable(Nestable db) { + _nested = true; + + if (_lockState->nestableCount()) { + // we are nested in our locking of local. previous lock could be read OR write lock on local. + } + else { + _lockState->lockedNestable(db, -1); + fassert(16133,_weLocked==0); + _weLocked = nestableLocks[db]; + _weLocked->lock_shared(); + } + } + + void Lock::DBWrite::lockOtherRead(const StringData& db) { + fassert(18517, !db.empty()); + + // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. + if( _lockState->otherCount() ) { + // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here + // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern. + invariant(db == _lockState->otherName()); + return; + } + + // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last + invariant(_lockState->nestableCount() == 0); + + if (db != _lockState->otherName()) { + DBLocksMap::ref r(dblocks); + WrapperForRWLock*& lock = r[db]; + if (lock == NULL) { + lock = new WrapperForRWLock(db); + } + + _lockState->lockedOther(db, -1, lock); + } + else { + DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } + _lockState->lockedOther(-1); + } + + fassert(18515, _weLocked == 0); + _lockState->otherLock()->lock_shared(); + _weLocked = _lockState->otherLock(); + } + + void Lock::DBWrite::lockOtherWrite(const StringData& db) { + fassert(16252, !db.empty()); + + // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. + if (_lockState->otherCount()) { + // nested. if/when we do temprelease with DBWrite we will need to increment here + // (so we can not release or assert if nested). + invariant(db == _lockState->otherName()); + return; + } + + // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last + invariant(_lockState->nestableCount() == 0); + + if (db != _lockState->otherName()) { + DBLocksMap::ref r(dblocks); + WrapperForRWLock*& lock = r[db]; + if (lock == NULL) { + lock = new WrapperForRWLock(db); + } + + _lockState->lockedOther(db, 1, lock); + } + else { + DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } + _lockState->lockedOther(1); + } + + fassert(16134,_weLocked==0); + + _lockState->otherLock()->lock(); + _weLocked = _lockState->otherLock(); + } + + static Lock::Nestable n(const StringData& db) { + if( db == "local" ) + return Lock::local; + if( db == "admin" ) + return Lock::admin; + return Lock::notnestable; + } + + void Lock::DBWrite::lockDB(const string& ns) { + fassert( 16253, !ns.empty() ); + + Acquiring a(this, *_lockState); + _locked_W=false; + _locked_w=false; + _weLocked=0; + + invariant(!_lockState->hasAnyReadLock()); + + if (_lockState->isW()) + return; + + StringData db = nsToDatabaseSubstring( ns ); + Nestable nested = n(db); + if( nested == admin ) { + // we can't nestedly lock both admin and local as implemented. so lock_W. + qlk.lock_W(_lockState); + _locked_W = true; + return; + } + + if (!nested) { + if (_isIntentWrite) { + lockOtherRead(db); + } + else { + lockOtherWrite(db); + } + } + + lockTop(); + if( nested ) + lockNestable(nested); + } + + void Lock::DBRead::lockDB(const string& ns) { + fassert( 16254, !ns.empty() ); + + Acquiring a(this, *_lockState); + _locked_r=false; + _weLocked=0; + + if (_lockState->isRW()) + return; + + StringData db = nsToDatabaseSubstring(ns); + Nestable nested = n(db); + if( !nested ) + lockOther(db); + lockTop(); + if( nested ) + lockNestable(nested); + } + + Lock::DBWrite::DBWrite(LockState* lockState, const StringData& ns, bool intentWrite) + : ScopedLock(lockState, 'w'), + _isIntentWrite(intentWrite), + _what(ns.toString()), + _nested(false) { + lockDB(_what); + } + + Lock::DBRead::DBRead(LockState* lockState, const StringData& ns) + : ScopedLock(lockState, 'r' ), _what(ns.toString()), _nested(false) { + lockDB( _what ); + } + + Lock::DBWrite::~DBWrite() { + unlockDB(); + } + Lock::DBRead::~DBRead() { + unlockDB(); + } + + void Lock::DBWrite::unlockDB() { + if( _weLocked ) { + recordTime(); // for lock stats + + if ( _nested ) + _lockState->unlockedNestable(); + else + _lockState->unlockedOther(); + + _weLocked->unlock(); + } + + if( _locked_w ) { + wassert(_lockState->threadState() == 'w'); + _lockState->unlocked(); + qlk.q.unlock_w(); + } + + if( _locked_W ) { + qlk.unlock_W(_lockState); + } + + _weLocked = 0; + _locked_W = _locked_w = false; + } + void Lock::DBRead::unlockDB() { + if( _weLocked ) { + recordTime(); // for lock stats + + if( _nested ) + _lockState->unlockedNestable(); + else + _lockState->unlockedOther(); + + _weLocked->unlock_shared(); + } + + if( _locked_r ) { + wassert(_lockState->threadState() == 'r'); + _lockState->unlocked(); + qlk.q.unlock_r(); + } + _weLocked = 0; + _locked_r = false; + } + + void Lock::DBWrite::lockTop() { + switch (_lockState->threadState()) { + case 'w': + break; + default: + verify(false); + case 0 : + verify(_lockState->threadState() == 0); + _lockState->lockedStart('w'); + qlk.q.lock_w(); + _locked_w = true; + } + } + void Lock::DBRead::lockTop() { + switch (_lockState->threadState()) { + case 'r': + case 'w': + break; + default: + verify(false); + case 0 : + verify(_lockState->threadState() == 0); + _lockState->lockedStart('r'); + qlk.q.lock_r(); + _locked_r = true; + } + } + + void Lock::DBRead::lockOther(const StringData& db) { + fassert( 16255, !db.empty() ); + + // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. + if( _lockState->otherCount() ) { + // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here + // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern. + invariant(db == _lockState->otherName()); + return; + } + + // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last + invariant(_lockState->nestableCount() == 0); + + if (db != _lockState->otherName()) { + DBLocksMap::ref r(dblocks); + WrapperForRWLock*& lock = r[db]; + if (lock == NULL) { + lock = new WrapperForRWLock(db); + } + + _lockState->lockedOther(db, -1, lock); + } + else { + DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } + _lockState->lockedOther(-1); + } + + fassert(16135,_weLocked==0); + _lockState->otherLock()->lock_shared(); + _weLocked = _lockState->otherLock(); + } + + Lock::UpgradeGlobalLockToExclusive::UpgradeGlobalLockToExclusive(LockState* lockState) + : _lockState(lockState) { + fassert( 16187, _lockState->threadState() == 'w' ); + + // We're about to temporarily drop w, so stop the lock time stopwatch + _lockState->recordLockTime(); + + _gotUpgrade = qlk.w_to_X(); + if ( _gotUpgrade ) { + _lockState->changeLockState('W'); + _lockState->resetLockTime(); + } + } + + Lock::UpgradeGlobalLockToExclusive::~UpgradeGlobalLockToExclusive() { + if ( _gotUpgrade ) { + fassert(16188, _lockState->threadState() == 'W'); + _lockState->recordLockTime(); + qlk.X_to_w(); + _lockState->changeLockState('w'); + } + else { + fassert(16189, _lockState->threadState() == 'w'); + } + + // Start recording lock time again + _lockState->resetLockTime(); + } + + writelocktry::writelocktry(LockState* lockState, int tryms) : + _got( false ), + _dbwlock( NULL ) + { + try { + _dbwlock.reset(new Lock::GlobalWrite(lockState, tryms)); + } + catch ( DBTryLockTimeoutException & ) { + return; + } + _got = true; + } + + writelocktry::~writelocktry() { + + } + + // note: the 'already' concept here might be a bad idea as a temprelease wouldn't notice it is nested then + readlocktry::readlocktry(LockState* lockState, int tryms) : + _got( false ), + _dbrlock( NULL ) + { + try { + _dbrlock.reset(new Lock::GlobalRead(lockState, tryms)); + } + catch ( DBTryLockTimeoutException & ) { + return; + } + _got = true; + } + + readlocktry::~readlocktry() { + + } + + /** + * This is passed to the iterator for global environments and aggregates information about the + * locks which are currently being held or waited on. + */ + class LockStateAggregator : public GlobalEnvironmentExperiment::ProcessOperationContext { + public: + LockStateAggregator(bool blockedOnly) + : numWriteLocked(0), + numReadLocked(0), + _blockedOnly(blockedOnly) { + + } + + virtual void processOpContext(OperationContext* txn) { + if (_blockedOnly && !txn->lockState()->hasLockPending()) { + return; + } + + if (txn->lockState()->isWriteLocked()) { + numWriteLocked++; + } + else { + numReadLocked++; + } + } + + int numWriteLocked; + int numReadLocked; + + private: + const bool _blockedOnly; + }; + + + class GlobalLockServerStatusSection : public ServerStatusSection { + public: + GlobalLockServerStatusSection() : ServerStatusSection( "globalLock" ){ + _started = curTimeMillis64(); + } + + virtual bool includeByDefault() const { return true; } + + virtual BSONObj generateSection( const BSONElement& configElement ) const { + BSONObjBuilder t; + + t.append( "totalTime" , (long long)(1000 * ( curTimeMillis64() - _started ) ) ); + t.append( "lockTime" , Lock::globalLockStat()->getTimeLocked( 'W' ) ); + + // This returns the blocked lock states + { + BSONObjBuilder ttt( t.subobjStart( "currentQueue" ) ); + + LockStateAggregator blocked(true); + getGlobalEnvironment()->forEachOperationContext(&blocked); + + ttt.append("total", blocked.numReadLocked + blocked.numWriteLocked); + ttt.append("readers", blocked.numReadLocked); + ttt.append("writers", blocked.numWriteLocked); + ttt.done(); + } + + // This returns all the active clients (including those holding locks) + { + BSONObjBuilder ttt( t.subobjStart( "activeClients" ) ); + + LockStateAggregator active(false); + getGlobalEnvironment()->forEachOperationContext(&active); + + ttt.append("total", active.numReadLocked + active.numWriteLocked); + ttt.append("readers", active.numReadLocked); + ttt.append("writers", active.numWriteLocked); + ttt.done(); + } + + return t.obj(); + } + + private: + unsigned long long _started; + + } globalLockServerStatusSection; + + class LockStatsServerStatusSection : public ServerStatusSection { + public: + LockStatsServerStatusSection() : ServerStatusSection( "locks" ){} + virtual bool includeByDefault() const { return true; } + + BSONObj generateSection( const BSONElement& configElement ) const { + BSONObjBuilder b; + b.append(".", qlk.stats.report()); + b.append("admin", nestableLocks[Lock::admin]->getStats().report()); + b.append("local", nestableLocks[Lock::local]->getStats().report()); + { + DBLocksMap::ref r(dblocks); + for( DBLocksMap::const_iterator i = r.r.begin(); i != r.r.end(); ++i ) { + b.append(i->first, i->second->getStats().report()); + } + } + return b.obj(); + } + + } lockStatsServerStatusSection; +} diff --git a/src/mongo/db/concurrency/d_concurrency.h b/src/mongo/db/concurrency/d_concurrency.h new file mode 100644 index 00000000000..03940bb13a8 --- /dev/null +++ b/src/mongo/db/concurrency/d_concurrency.h @@ -0,0 +1,253 @@ +// @file d_concurrency.h + +/** +* Copyright (C) 2008-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 . +* +* 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. +*/ + + +// only used by mongod, thus the name ('d') +// (also used by dbtests test binary, which is running mongod test code) + +#pragma once + +#include "mongo/base/string_data.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/concurrency/lock_stat.h" +#include "mongo/util/concurrency/mutex.h" +#include "mongo/util/concurrency/rwlock.h" + +namespace mongo { + + class WrapperForRWLock; + class LockState; + + class Lock : boost::noncopyable { + public: + enum Nestable { notnestable=0, local, admin }; + + static LockStat* globalLockStat(); + static LockStat* nestableLockStat( Nestable db ); + + class ScopedLock; + + // note: avoid TempRelease when possible. not a good thing. + struct TempRelease { + TempRelease(LockState* lockState); + ~TempRelease(); + const bool cant; // true if couldn't because of recursive locking + + // Not owned + LockState* _lockState; + ScopedLock *scopedLk; + }; + + /** turn on "parallel batch writer mode". blocks all other threads. this mode is off + by default. note only one thread creates a ParallelBatchWriterMode object; the rest just + call iAmABatchParticipant(). Note that this lock is not released on a temprelease, just + the normal lock things below. + */ + class ParallelBatchWriterMode : boost::noncopyable { + RWLockRecursive::Exclusive _lk; + public: + ParallelBatchWriterMode() : _lk(_batchLock) {} + static void iAmABatchParticipant(LockState* lockState); + static RWLockRecursive &_batchLock; + }; + + public: + + class ScopedLock : boost::noncopyable { + public: + virtual ~ScopedLock(); + + /** @return micros since we started acquiring */ + long long acquireFinished( LockStat* stat ); + + // Accrue elapsed lock time since last we called reset + void recordTime(); + // Start recording a new period, starting now() + void resetTime(); + + protected: + explicit ScopedLock(LockState* lockState, char type ); + + private: + friend struct TempRelease; + void tempRelease(); // TempRelease class calls these + void relock(); + + protected: + virtual void _tempRelease() = 0; + virtual void _relock() = 0; + + LockState* _lockState; + + private: + + class ParallelBatchWriterSupport : boost::noncopyable { + public: + ParallelBatchWriterSupport(LockState* lockState); + + private: + void tempRelease(); + void relock(); + + LockState* _lockState; + scoped_ptr _lk; + friend class ScopedLock; + }; + + ParallelBatchWriterSupport _pbws_lk; + + void _recordTime( long long micros ); + Timer _timer; + char _type; // 'r','w','R','W' + LockStat* _stat; // the stat for the relevant lock to increment when we're done + }; + + // note that for these classes recursive locking is ok if the recursive locking "makes sense" + // i.e. you could grab globalread after globalwrite. + + class GlobalWrite : public ScopedLock { + bool noop; + protected: + void _tempRelease(); + void _relock(); + public: + // stopGreed is removed and does NOT work + // timeoutms is only for writelocktry -- deprecated -- do not use + GlobalWrite(LockState* lockState, int timeoutms = -1); + virtual ~GlobalWrite(); + void downgrade(); // W -> R + void upgrade(); // caution see notes + }; + + class GlobalRead : public ScopedLock { // recursive is ok + public: + bool noop; + protected: + void _tempRelease(); + void _relock(); + public: + // timeoutms is only for readlocktry -- deprecated -- do not use + GlobalRead(LockState* lockState, int timeoutms = -1); + virtual ~GlobalRead(); + }; + + // lock this database. do not shared_lock globally first, that is handledin herein. + class DBWrite : public ScopedLock { + /** + * flow + * 1) lockDB + * a) lockTop + * b) lockNestable or lockOther + * 2) unlockDB + */ + + void lockTop(); + void lockNestable(Nestable db); + void lockOtherWrite(const StringData& db); + void lockOtherRead(const StringData& db); + void lockDB(const std::string& ns); + void unlockDB(); + + protected: + void _tempRelease(); + void _relock(); + + public: + DBWrite(LockState* lockState, const StringData& dbOrNs, bool intentWrite = false); + virtual ~DBWrite(); + + private: + bool _locked_w; + bool _locked_W; + bool _isIntentWrite; + WrapperForRWLock *_weLocked; + const std::string _what; + bool _nested; + }; + + // lock this database for reading. do not shared_lock globally first, that is handledin herein. + class DBRead : public ScopedLock { + void lockTop(); + void lockNestable(Nestable db); + void lockOther(const StringData& db); + void lockDB(const std::string& ns); + void unlockDB(); + + protected: + void _tempRelease(); + void _relock(); + + public: + DBRead(LockState* lockState, const StringData& dbOrNs); + virtual ~DBRead(); + + private: + bool _locked_r; + WrapperForRWLock *_weLocked; + std::string _what; + bool _nested; + + }; + + /** + * Acquires a previously acquired intent-X (lower-case 'w') GlobalWrite lock to upper-case + * 'W' lock. Effectively means "stop the world". + */ + class UpgradeGlobalLockToExclusive : private boost::noncopyable { + public: + UpgradeGlobalLockToExclusive(LockState* lockState); + ~UpgradeGlobalLockToExclusive(); + + bool gotUpgrade() const { return _gotUpgrade; } + + private: + LockState* _lockState; + bool _gotUpgrade; + }; + }; + + class readlocktry : boost::noncopyable { + bool _got; + scoped_ptr _dbrlock; + public: + readlocktry(LockState* lockState, int tryms); + ~readlocktry(); + bool got() const { return _got; } + }; + + class writelocktry : boost::noncopyable { + bool _got; + scoped_ptr _dbwlock; + public: + writelocktry(LockState* lockState, int tryms); + ~writelocktry(); + bool got() const { return _got; } + }; +} diff --git a/src/mongo/db/concurrency/lock_stat.cpp b/src/mongo/db/concurrency/lock_stat.cpp new file mode 100644 index 00000000000..7a6b3355231 --- /dev/null +++ b/src/mongo/db/concurrency/lock_stat.cpp @@ -0,0 +1,127 @@ +// lock_stat.cpp + +/** +* Copyright (C) 2008 10gen 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 . +* +* 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_stat.h" + +#include "mongo/db/jsobj.h" + +namespace mongo { + + BSONObj LockStat::report() const { + BSONObjBuilder b; + + BSONObjBuilder t( b.subobjStart( "timeLockedMicros" ) ); + _append( b, timeLocked, timeAcquiring ); + t.done(); + + BSONObjBuilder a( b.subobjStart( "timeAcquiringMicros" ) ); + _append( a, timeAcquiring, timeLocked ); + a.done(); + + return b.obj(); + } + + void LockStat::report( StringBuilder& builder ) const { + bool prefixPrinted = false; + for ( int i=0; i < N; i++ ) { + if ( timeLocked[i].load() == 0 ) + continue; + + if ( ! prefixPrinted ) { + builder << "locks(micros)"; + prefixPrinted = true; + } + + builder << ' ' << nameFor( i ) << ':' << timeLocked[i].load(); + } + + } + + void LockStat::_append( BSONObjBuilder& builder, + const AtomicInt64* data, + const AtomicInt64* additionalIndicator ) { + + if ( data[0].load() || + data[1].load() || + ( additionalIndicator && ( additionalIndicator[0].load() || + additionalIndicator[1].load() ) ) ) { + builder.append( "R" , data[0].load() ); + builder.append( "W" , data[1].load() ); + } + + if ( data[2].load() || + data[3].load() || + ( additionalIndicator && ( additionalIndicator[2].load() || + additionalIndicator[3].load() ) ) ) { + builder.append( "r" , data[2].load() ); + builder.append( "w" , data[3].load() ); + } + } + + unsigned LockStat::mapNo(char type) { + switch( type ) { + case 'R' : return 0; + case 'W' : return 1; + case 'r' : return 2; + case 'w' : return 3; + default: ; + } + fassert(16146,false); + return 0; + } + + char LockStat::nameFor(unsigned offset) { + switch ( offset ) { + case 0: return 'R'; + case 1: return 'W'; + case 2: return 'r'; + case 3: return 'w'; + } + fassertFailed(16339); + } + + + void LockStat::recordAcquireTimeMicros( char type , long long micros ) { + timeAcquiring[mapNo(type)].fetchAndAdd( micros ); + } + void LockStat::recordLockTimeMicros( char type , long long micros ) { + timeLocked[mapNo(type)].fetchAndAdd( micros ); + } + + void LockStat::reset() { + for ( int i = 0; i < N; i++ ) { + timeAcquiring[i].store(0); + timeLocked[i].store(0); + } + } +} diff --git a/src/mongo/db/concurrency/lock_stat.h b/src/mongo/db/concurrency/lock_stat.h new file mode 100644 index 00000000000..9407ba2df1a --- /dev/null +++ b/src/mongo/db/concurrency/lock_stat.h @@ -0,0 +1,68 @@ +// lock_stat.h + +/** +* Copyright (C) 2008 10gen 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 . +* +* 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 "mongo/bson/util/builder.h" +#include "mongo/platform/atomic_word.h" +#include "mongo/util/timer.h" + +namespace mongo { + + class BSONObj; + + class LockStat { + enum { N = 4 }; + public: + void recordAcquireTimeMicros( char type , long long micros ); + void recordLockTimeMicros( char type , long long micros ); + + void reset(); + + BSONObj report() const; + void report( StringBuilder& builder ) const; + + long long getTimeLocked( char type ) const { return timeLocked[mapNo(type)].load(); } + private: + static void _append( BSONObjBuilder& builder, + const AtomicInt64* data, + const AtomicInt64* additionalIndicator ); + + // RWrw + // in micros + AtomicInt64 timeAcquiring[N]; + AtomicInt64 timeLocked[N]; + + static unsigned mapNo(char type); + static char nameFor(unsigned offset); + }; + +} diff --git a/src/mongo/db/concurrency/lock_state.cpp b/src/mongo/db/concurrency/lock_state.cpp new file mode 100644 index 00000000000..4abadea64c1 --- /dev/null +++ b/src/mongo/db/concurrency/lock_state.cpp @@ -0,0 +1,311 @@ +// lockstate.cpp + +/** +* Copyright (C) 2008 10gen 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 . +* +* 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/db/concurrency/lock_state.h" + +#include "mongo/db/namespace_string.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + + +namespace mongo { + + LockState::LockState() + : _batchWriter(false), + _recursive(0), + _threadState(0), + _whichNestable( Lock::notnestable ), + _nestableCount(0), + _otherCount(0), + _otherLock(NULL), + _scopedLk(NULL), + _lockPending(false), + _lockPendingParallelWriter(false) + { + } + + bool LockState::isRW() const { + return _threadState == 'R' || _threadState == 'W'; + } + + bool LockState::isW() const { + return _threadState == 'W'; + } + + bool LockState::hasAnyReadLock() const { + return _threadState == 'r' || _threadState == 'R'; + } + + bool LockState::isLocked( const StringData& ns ) const { + char db[MaxDatabaseNameLen]; + nsToDatabase(ns, db); + + DEV verify( _otherName.find( '.' ) == string::npos ); // XXX this shouldn't be here, but somewhere + if ( _otherCount && db == _otherName ) + return true; + + if ( _nestableCount ) { + if ( mongoutils::str::equals( db , "local" ) ) + return _whichNestable == Lock::local; + if ( mongoutils::str::equals( db , "admin" ) ) + return _whichNestable == Lock::admin; + } + + return false; + } + + bool LockState::isLocked() const { + return threadState() != 0; + } + + bool LockState::isWriteLocked() const { + return (threadState() == 'W' || threadState() == 'w'); + } + + bool LockState::isWriteLocked(const StringData& ns) const { + if (isWriteLocked()) { + return true; + } + + return isLocked(ns); + } + + bool LockState::isAtLeastReadLocked(const StringData& ns) const { + if (threadState() == 'R' || threadState() == 'W') + return true; // global + if (threadState() == 0) + return false; + return isLocked(ns); + } + + bool LockState::isLockedForCommitting() const { + return threadState() == 'R' || threadState() == 'W'; + } + + bool LockState::isRecursive() const { + return recursiveCount() > 1; + } + + void LockState::assertWriteLocked(const StringData& ns) const { + if (!isWriteLocked(ns)) { + dump(); + msgasserted( + 16105, mongoutils::str::stream() << "expected to be write locked for " << ns); + } + } + + void LockState::assertAtLeastReadLocked(const StringData& ns) const { + if (!isAtLeastReadLocked(ns)) { + log() << "error expected " << ns << " to be locked " << endl; + dump(); + msgasserted( + 16104, mongoutils::str::stream() << "expected to be read locked for " << ns); + } + } + + void LockState::lockedStart( char newState ) { + _threadState = newState; + } + void LockState::unlocked() { + _threadState = 0; + } + + void LockState::changeLockState( char newState ) { + fassert( 16169 , _threadState != 0 ); + _threadState = newState; + } + + static string kind(int n) { + if( n > 0 ) + return "W"; + if( n < 0 ) + return "R"; + return "?"; + } + + BSONObj LockState::reportState() { + BSONObjBuilder b; + reportState(&b); + + return b.obj(); + } + + /** Note: this is is called by the currentOp command, which is a different + thread. So be careful about thread safety here. For example reading + this->otherName would not be safe as-is! + */ + void LockState::reportState(BSONObjBuilder* res) { + BSONObjBuilder b; + if( _threadState ) { + char buf[2]; + buf[0] = _threadState; + buf[1] = 0; + b.append("^", buf); + } + if( _nestableCount ) { + string s = "?"; + if( _whichNestable == Lock::local ) + s = "^local"; + else if( _whichNestable == Lock::admin ) + s = "^admin"; + b.append(s, kind(_nestableCount)); + } + if( _otherCount ) { + WrapperForRWLock *k = _otherLock; + if( k ) { + string s = "^"; + s += k->name(); + b.append(s, kind(_otherCount)); + } + } + BSONObj o = b.obj(); + if (!o.isEmpty()) { + res->append("locks", o); + } + res->append("waitingForLock", _lockPending); + } + + void LockState::dump() const { + char s = _threadState; + stringstream ss; + ss << "lock status: "; + if( s == 0 ){ + ss << "unlocked"; + } + else { + ss << s; + if( _recursive ) { + ss << " recursive:" << _recursive; + } + ss << " otherCount:" << _otherCount; + if( _otherCount ) { + ss << " otherdb:" << _otherName; + } + if( _nestableCount ) { + ss << " nestableCount:" << _nestableCount << " which:"; + if( _whichNestable == Lock::local ) + ss << "local"; + else if( _whichNestable == Lock::admin ) + ss << "admin"; + else + ss << (int)_whichNestable; + } + } + log() << ss.str() << endl; + } + + void LockState::enterScopedLock( Lock::ScopedLock* lock ) { + _recursive++; + if ( _recursive == 1 ) { + fassert(16115, _scopedLk == 0); + _scopedLk = lock; + } + } + + Lock::ScopedLock* LockState::leaveScopedLock() { + _recursive--; + dassert( _recursive < 10000 ); + Lock::ScopedLock* temp = _scopedLk; + + if ( _recursive > 0 ) { + return NULL; + } + + _scopedLk = NULL; + return temp; + } + + void LockState::lockedNestable( Lock::Nestable what , int type) { + verify( type ); + _whichNestable = what; + _nestableCount += type; + } + + void LockState::unlockedNestable() { + _whichNestable = Lock::notnestable; + _nestableCount = 0; + } + + void LockState::lockedOther( int type ) { + fassert( 16231 , _otherCount == 0 ); + _otherCount = type; + } + + void LockState::lockedOther( const StringData& other , int type , WrapperForRWLock* lock ) { + fassert( 16170 , _otherCount == 0 ); + _otherName = other.toString(); + _otherCount = type; + _otherLock = lock; + } + + void LockState::unlockedOther() { + // we leave _otherName and _otherLock set as + // _otherLock exists to cache a pointer + _otherCount = 0; + } + + LockStat* LockState::getRelevantLockStat() { + if ( _whichNestable ) + return Lock::nestableLockStat( _whichNestable ); + + if ( _otherCount && _otherLock ) + return &_otherLock->getStats(); + + if ( isRW() ) + return Lock::globalLockStat(); + + return 0; + } + + + Acquiring::Acquiring( Lock::ScopedLock* lock, LockState& ls ) + : _lock( lock ), _ls( ls ){ + _ls._lockPending = true; + } + + Acquiring::~Acquiring() { + _ls._lockPending = false; + LockStat* stat = _ls.getRelevantLockStat(); + if ( stat && _lock ) { + // increment the global stats for this counter + stat->recordAcquireTimeMicros( _ls.threadState(), _lock->acquireFinished( stat ) ); + } + } + + AcquiringParallelWriter::AcquiringParallelWriter( LockState& ls ) + : _ls( ls ) { + _ls._lockPendingParallelWriter = true; + } + + AcquiringParallelWriter::~AcquiringParallelWriter() { + _ls._lockPendingParallelWriter = false; + } + +} diff --git a/src/mongo/db/concurrency/lock_state.h b/src/mongo/db/concurrency/lock_state.h new file mode 100644 index 00000000000..adc1716faa7 --- /dev/null +++ b/src/mongo/db/concurrency/lock_state.h @@ -0,0 +1,178 @@ +// lock_state.h + +/** +* Copyright (C) 2008 10gen 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 . +* +* 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 "mongo/db/concurrency/d_concurrency.h" + + +namespace mongo { + + class Acquiring; + + // per thread + class LockState { + public: + LockState(); + + void dump() const; + + BSONObj reportState(); + void reportState(BSONObjBuilder* b); + + unsigned recursiveCount() const { return _recursive; } + + /** + * @return 0 rwRW + */ + char threadState() const { return _threadState; } + + bool isRW() const; // RW + bool isW() const; // W + bool hasAnyReadLock() const; // explicitly rR + + bool isLocked(const StringData& ns) const; // rwRW + bool isLocked() const; + bool isWriteLocked() const; + bool isWriteLocked(const StringData& ns) const; + bool isAtLeastReadLocked(const StringData& ns) const; + bool isLockedForCommitting() const; + bool isRecursive() const; + + void assertWriteLocked(const StringData& ns) const; + void assertAtLeastReadLocked(const StringData& ns) const; + + /** pending means we are currently trying to get a lock */ + bool hasLockPending() const { return _lockPending || _lockPendingParallelWriter; } + + // ---- + + + void lockedStart( char newState ); // RWrw + void unlocked(); // _threadState = 0 + + /** + * you have to be locked already to call this + * this is mostly for W_to_R or R_to_W + */ + void changeLockState( char newstate ); + + Lock::Nestable whichNestable() const { return _whichNestable; } + int nestableCount() const { return _nestableCount; } + + int otherCount() const { return _otherCount; } + const std::string& otherName() const { return _otherName; } + WrapperForRWLock* otherLock() const { return _otherLock; } + + void enterScopedLock( Lock::ScopedLock* lock ); + Lock::ScopedLock* leaveScopedLock(); + + void lockedNestable( Lock::Nestable what , int type ); + void unlockedNestable(); + void lockedOther( const StringData& db , int type , WrapperForRWLock* lock ); + void lockedOther( int type ); // "same lock as last time" case + void unlockedOther(); + bool _batchWriter; + + LockStat* getRelevantLockStat(); + void recordLockTime() { _scopedLk->recordTime(); } + void resetLockTime() { _scopedLk->resetTime(); } + + private: + unsigned _recursive; // we allow recursively asking for a lock; we track that here + + // global lock related + char _threadState; // 0, 'r', 'w', 'R', 'W' + + // db level locking related + Lock::Nestable _whichNestable; + int _nestableCount; // recursive lock count on local or admin db XXX - change name + + int _otherCount; // >0 means write lock, <0 read lock - XXX change name + std::string _otherName; // which database are we locking and working with (besides local/admin) + WrapperForRWLock* _otherLock; // so we don't have to check the map too often (the map has a mutex) + + // for temprelease + // for the nonrecursive case. otherwise there would be many + // the first lock goes here, which is ok since we can't yield recursive locks + Lock::ScopedLock* _scopedLk; + + bool _lockPending; + bool _lockPendingParallelWriter; + + friend class Acquiring; + friend class AcquiringParallelWriter; + }; + + class WrapperForRWLock : boost::noncopyable { + SimpleRWLock rw; + SimpleMutex m; + bool sharedLatching; + LockStat stats; + public: + std::string name() const { return rw.name; } + LockStat& getStats() { return stats; } + + WrapperForRWLock(const StringData& name) + : rw(name), m(name) { + // For the local datbase, all operations are short, + // either writing one entry, or doing a tail. + // In tests, use a SimpleMutex is much faster for the local db. + sharedLatching = name != "local"; + } + void lock() { if ( sharedLatching ) { rw.lock(); } else { m.lock(); } } + void lock_shared() { if ( sharedLatching ) { rw.lock_shared(); } else { m.lock(); } } + void unlock() { if ( sharedLatching ) { rw.unlock(); } else { m.unlock(); } } + void unlock_shared() { if ( sharedLatching ) { rw.unlock_shared(); } else { m.unlock(); } } + }; + + class ScopedLock; + + class Acquiring { + public: + Acquiring( Lock::ScopedLock* lock, LockState& ls ); + ~Acquiring(); + private: + Lock::ScopedLock* _lock; + LockState& _ls; + }; + + class AcquiringParallelWriter { + public: + AcquiringParallelWriter( LockState& ls ); + ~AcquiringParallelWriter(); + + private: + LockState& _ls; + }; + + +} diff --git a/src/mongo/db/d_concurrency.cpp b/src/mongo/db/d_concurrency.cpp deleted file mode 100644 index 2c7fcffd4ea..00000000000 --- a/src/mongo/db/d_concurrency.cpp +++ /dev/null @@ -1,831 +0,0 @@ -// @file d_concurrency.cpp - -/** -* Copyright (C) 2008-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 . -* -* 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/d_concurrency.h" - -#include "mongo/db/client.h" -#include "mongo/db/commands/server_status.h" -#include "mongo/db/curop.h" -#include "mongo/db/d_globals.h" -#include "mongo/db/global_environment_experiment.h" -#include "mongo/db/lockstat.h" -#include "mongo/db/namespace_string.h" -#include "mongo/db/server_parameters.h" -#include "mongo/db/operation_context.h" -#include "mongo/server.h" -#include "mongo/util/assert_util.h" -#include "mongo/util/concurrency/mapsf.h" -#include "mongo/util/concurrency/qlock.h" -#include "mongo/util/concurrency/rwlock.h" -#include "mongo/util/concurrency/threadlocal.h" -#include "mongo/util/log.h" -#include "mongo/util/mongoutils/str.h" -#include "mongo/util/stacktrace.h" - -// oplog locking -// no top level read locks -// system.profile writing -// oplog now -// yielding - -namespace mongo { - - class DBTryLockTimeoutException : public std::exception { - public: - DBTryLockTimeoutException() {} - virtual ~DBTryLockTimeoutException() throw() { } - }; - - /* dbname->lock - Currently these are never deleted - will linger if db was closed. (that should be fine.) - We don't put the lock inside the Database object as those can come and go with open and - closes and that would just add complexity. - Note there is no path concept for where the database is; if somehow you had two db's open - in different directories with the same name, it will be ok but they are sharing a lock - then. - */ - typedef mapsf< StringMap > DBLocksMap; - static DBLocksMap dblocks; - - /* we don't want to touch dblocks too much as a mutex is involved. thus party for that, - this is here... - */ - WrapperForRWLock *nestableLocks[] = { - 0, - new WrapperForRWLock("local"), - new WrapperForRWLock("admin") - }; - - LockStat* Lock::nestableLockStat( Nestable db ) { - return &nestableLocks[db]->getStats(); - } - - class WrapperForQLock { - public: - QLock q; - LockStat stats; - - void lock_R(LockState* lockState) { - invariant(lockState->threadState() == 0); - lockState->lockedStart('R'); - q.lock_R(); - } - - void lock_W(LockState* lockState) { - if (lockState->threadState()) { - log() << "can't lock_W, threadState=" << (int)lockState->threadState() << endl; - fassert(16114,false); - } - - lockState->lockedStart('W'); - q.lock_W(); - } - - // how to count try's that fail is an interesting question. we should get rid of try(). - bool lock_R_try(LockState* lockState, int millis) { - verify(lockState->threadState() == 0); - bool got = q.lock_R_try(millis); - if (got) { - lockState->lockedStart('R'); - } - return got; - } - - bool lock_W_try(LockState* lockState, int millis) { - verify(lockState->threadState() == 0); - bool got = q.lock_W_try(millis); - if( got ) { - lockState->lockedStart('W'); - } - return got; - } - - void unlock_R(LockState* lockState) { - wassert(lockState->threadState() == 'R'); - lockState->unlocked(); - q.unlock_R(); - } - - void unlock_W(LockState* lockState) { - wassert(lockState->threadState() == 'W'); - lockState->unlocked(); - q.unlock_W(); - } - - // todo timing stats? : - void W_to_R() { q.W_to_R(); } - void R_to_W() { q.R_to_W(); } - bool w_to_X() { return q.w_to_X(); } - void X_to_w() { q.X_to_w(); } - }; - - static WrapperForQLock& qlk = *new WrapperForQLock(); - LockStat* Lock::globalLockStat() { - return &qlk.stats; - } - - - RWLockRecursive &Lock::ParallelBatchWriterMode::_batchLock = *(new RWLockRecursive("special")); - void Lock::ParallelBatchWriterMode::iAmABatchParticipant(LockState* lockState) { - lockState->_batchWriter = true; - } - - Lock::ScopedLock::ParallelBatchWriterSupport::ParallelBatchWriterSupport(LockState* lockState) - : _lockState(lockState) { - relock(); - } - - void Lock::ScopedLock::ParallelBatchWriterSupport::tempRelease() { - _lk.reset( 0 ); - } - - void Lock::ScopedLock::ParallelBatchWriterSupport::relock() { - if (!_lockState->_batchWriter) { - AcquiringParallelWriter a(*_lockState); - _lk.reset( new RWLockRecursive::Shared(ParallelBatchWriterMode::_batchLock) ); - } - } - - - Lock::ScopedLock::ScopedLock(LockState* lockState, char type) - : _lockState(lockState), _pbws_lk(lockState), _type(type), _stat(0) { - - _lockState->enterScopedLock(this); - } - - Lock::ScopedLock::~ScopedLock() { - int prevCount = _lockState->recursiveCount(); - Lock::ScopedLock* what = _lockState->leaveScopedLock(); - fassert( 16171 , prevCount != 1 || what == this ); - } - - long long Lock::ScopedLock::acquireFinished( LockStat* stat ) { - long long acquisitionTime = _timer.micros(); - _timer.reset(); - _stat = stat; - - // increment the operation level statistics - cc().curop()->lockStat().recordAcquireTimeMicros( _type , acquisitionTime ); - - return acquisitionTime; - } - - void Lock::ScopedLock::tempRelease() { - long long micros = _timer.micros(); - _tempRelease(); - _pbws_lk.tempRelease(); - _recordTime( micros ); // might as well do after we unlock - } - - void Lock::ScopedLock::_recordTime( long long micros ) { - if ( _stat ) - _stat->recordLockTimeMicros( _type , micros ); - cc().curop()->lockStat().recordLockTimeMicros( _type , micros ); - } - - void Lock::ScopedLock::recordTime() { - _recordTime(_timer.micros()); - } - - void Lock::ScopedLock::resetTime() { - _timer.reset(); - } - - void Lock::ScopedLock::relock() { - _pbws_lk.relock(); - resetTime(); - _relock(); - } - - Lock::TempRelease::TempRelease(LockState* lockState) - : cant(lockState->isRecursive()), _lockState(lockState) { - - if( cant ) - return; - - fassert(16116, _lockState->recursiveCount() == 1); - fassert(16117, _lockState->threadState() != 0); - - scopedLk = _lockState->leaveScopedLock(); - fassert(16118, scopedLk); - - invariant(_lockState == scopedLk->_lockState); - - scopedLk->tempRelease(); - } - Lock::TempRelease::~TempRelease() - { - if( cant ) - return; - - fassert(16119, scopedLk); - fassert(16120, _lockState->threadState() == 0); - - _lockState->enterScopedLock(scopedLk); - scopedLk->relock(); - } - - void Lock::GlobalWrite::_tempRelease() { - fassert(16121, !noop); - char ts = _lockState->threadState(); - fassert(16122, ts != 'R'); // indicates downgraded; not allowed with temprelease - fassert(16123, ts == 'W'); - qlk.unlock_W(_lockState); - } - void Lock::GlobalWrite::_relock() { - fassert(16125, !noop); - char ts = _lockState->threadState(); - fassert(16126, ts == 0); - Acquiring a(this, *_lockState); - qlk.lock_W(_lockState); - } - - void Lock::GlobalRead::_tempRelease() { - fassert(16127, !noop); - char ts = _lockState->threadState(); - fassert(16128, ts == 'R'); - qlk.unlock_R(_lockState); - } - void Lock::GlobalRead::_relock() { - fassert(16129, !noop); - char ts = _lockState->threadState(); - fassert(16130, ts == 0); - Acquiring a(this, *_lockState); - qlk.lock_R(_lockState); - } - - void Lock::DBWrite::_tempRelease() { - unlockDB(); - } - void Lock::DBWrite::_relock() { - lockDB(_what); - } - void Lock::DBRead::_tempRelease() { - unlockDB(); - } - void Lock::DBRead::_relock() { - lockDB(_what); - } - - Lock::GlobalWrite::GlobalWrite(LockState* lockState, int timeoutms) - : ScopedLock(lockState, 'W') { - - char ts = _lockState->threadState(); - noop = false; - if( ts == 'W' ) { - noop = true; - return; - } - dassert( ts == 0 ); - - Acquiring a(this, *_lockState); - - if ( timeoutms != -1 ) { - bool success = qlk.lock_W_try(_lockState, timeoutms); - if ( !success ) throw DBTryLockTimeoutException(); - } - else { - qlk.lock_W(_lockState); - } - } - Lock::GlobalWrite::~GlobalWrite() { - if( noop ) { - return; - } - recordTime(); // for lock stats - if (_lockState->threadState() == 'R') { // we downgraded - qlk.unlock_R(_lockState); - } - else { - qlk.unlock_W(_lockState); - } - } - void Lock::GlobalWrite::downgrade() { - verify( !noop ); - verify(_lockState->threadState() == 'W'); - - qlk.W_to_R(); - _lockState->changeLockState('R'); - } - - // you will deadlock if 2 threads doing this - void Lock::GlobalWrite::upgrade() { - verify( !noop ); - verify(_lockState->threadState() == 'R'); - - qlk.R_to_W(); - _lockState->changeLockState('W'); - } - - Lock::GlobalRead::GlobalRead(LockState* lockState, int timeoutms) - : ScopedLock(lockState, 'R') { - - char ts = _lockState->threadState(); - noop = false; - if( ts == 'R' || ts == 'W' ) { - noop = true; - return; - } - - Acquiring a(this, *_lockState); - - if ( timeoutms != -1 ) { - bool success = qlk.lock_R_try(_lockState, timeoutms); - if ( !success ) throw DBTryLockTimeoutException(); - } - else { - // we are unlocked in the qlock/top sense. lock_R will assert if we are in an in compatible state - qlk.lock_R(_lockState); - } - } - Lock::GlobalRead::~GlobalRead() { - if( !noop ) { - recordTime(); // for lock stats - qlk.unlock_R(_lockState); - } - } - - void Lock::DBWrite::lockNestable(Nestable db) { - _nested = true; - - if (_lockState->nestableCount()) { - if( db != _lockState->whichNestable() ) { - error() << "can't lock local and admin db at the same time " << (int) db << ' ' << (int) _lockState->whichNestable() << endl; - fassert(16131,false); - } - verify( _lockState->nestableCount() > 0 ); - } - else { - fassert(16132,_weLocked==0); - _lockState->lockedNestable(db, 1); - _weLocked = nestableLocks[db]; - _weLocked->lock(); - } - } - void Lock::DBRead::lockNestable(Nestable db) { - _nested = true; - - if (_lockState->nestableCount()) { - // we are nested in our locking of local. previous lock could be read OR write lock on local. - } - else { - _lockState->lockedNestable(db, -1); - fassert(16133,_weLocked==0); - _weLocked = nestableLocks[db]; - _weLocked->lock_shared(); - } - } - - void Lock::DBWrite::lockOtherRead(const StringData& db) { - fassert(18517, !db.empty()); - - // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. - if( _lockState->otherCount() ) { - // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here - // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern. - invariant(db == _lockState->otherName()); - return; - } - - // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last - invariant(_lockState->nestableCount() == 0); - - if (db != _lockState->otherName()) { - DBLocksMap::ref r(dblocks); - WrapperForRWLock*& lock = r[db]; - if (lock == NULL) { - lock = new WrapperForRWLock(db); - } - - _lockState->lockedOther(db, -1, lock); - } - else { - DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } - _lockState->lockedOther(-1); - } - - fassert(18515, _weLocked == 0); - _lockState->otherLock()->lock_shared(); - _weLocked = _lockState->otherLock(); - } - - void Lock::DBWrite::lockOtherWrite(const StringData& db) { - fassert(16252, !db.empty()); - - // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. - if (_lockState->otherCount()) { - // nested. if/when we do temprelease with DBWrite we will need to increment here - // (so we can not release or assert if nested). - invariant(db == _lockState->otherName()); - return; - } - - // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last - invariant(_lockState->nestableCount() == 0); - - if (db != _lockState->otherName()) { - DBLocksMap::ref r(dblocks); - WrapperForRWLock*& lock = r[db]; - if (lock == NULL) { - lock = new WrapperForRWLock(db); - } - - _lockState->lockedOther(db, 1, lock); - } - else { - DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } - _lockState->lockedOther(1); - } - - fassert(16134,_weLocked==0); - - _lockState->otherLock()->lock(); - _weLocked = _lockState->otherLock(); - } - - static Lock::Nestable n(const StringData& db) { - if( db == "local" ) - return Lock::local; - if( db == "admin" ) - return Lock::admin; - return Lock::notnestable; - } - - void Lock::DBWrite::lockDB(const string& ns) { - fassert( 16253, !ns.empty() ); - - Acquiring a(this, *_lockState); - _locked_W=false; - _locked_w=false; - _weLocked=0; - - invariant(!_lockState->hasAnyReadLock()); - - if (_lockState->isW()) - return; - - StringData db = nsToDatabaseSubstring( ns ); - Nestable nested = n(db); - if( nested == admin ) { - // we can't nestedly lock both admin and local as implemented. so lock_W. - qlk.lock_W(_lockState); - _locked_W = true; - return; - } - - if (!nested) { - if (_isIntentWrite) { - lockOtherRead(db); - } - else { - lockOtherWrite(db); - } - } - - lockTop(); - if( nested ) - lockNestable(nested); - } - - void Lock::DBRead::lockDB(const string& ns) { - fassert( 16254, !ns.empty() ); - - Acquiring a(this, *_lockState); - _locked_r=false; - _weLocked=0; - - if (_lockState->isRW()) - return; - - StringData db = nsToDatabaseSubstring(ns); - Nestable nested = n(db); - if( !nested ) - lockOther(db); - lockTop(); - if( nested ) - lockNestable(nested); - } - - Lock::DBWrite::DBWrite(LockState* lockState, const StringData& ns, bool intentWrite) - : ScopedLock(lockState, 'w'), - _isIntentWrite(intentWrite), - _what(ns.toString()), - _nested(false) { - lockDB(_what); - } - - Lock::DBRead::DBRead(LockState* lockState, const StringData& ns) - : ScopedLock(lockState, 'r' ), _what(ns.toString()), _nested(false) { - lockDB( _what ); - } - - Lock::DBWrite::~DBWrite() { - unlockDB(); - } - Lock::DBRead::~DBRead() { - unlockDB(); - } - - void Lock::DBWrite::unlockDB() { - if( _weLocked ) { - recordTime(); // for lock stats - - if ( _nested ) - _lockState->unlockedNestable(); - else - _lockState->unlockedOther(); - - _weLocked->unlock(); - } - - if( _locked_w ) { - wassert(_lockState->threadState() == 'w'); - _lockState->unlocked(); - qlk.q.unlock_w(); - } - - if( _locked_W ) { - qlk.unlock_W(_lockState); - } - - _weLocked = 0; - _locked_W = _locked_w = false; - } - void Lock::DBRead::unlockDB() { - if( _weLocked ) { - recordTime(); // for lock stats - - if( _nested ) - _lockState->unlockedNestable(); - else - _lockState->unlockedOther(); - - _weLocked->unlock_shared(); - } - - if( _locked_r ) { - wassert(_lockState->threadState() == 'r'); - _lockState->unlocked(); - qlk.q.unlock_r(); - } - _weLocked = 0; - _locked_r = false; - } - - void Lock::DBWrite::lockTop() { - switch (_lockState->threadState()) { - case 'w': - break; - default: - verify(false); - case 0 : - verify(_lockState->threadState() == 0); - _lockState->lockedStart('w'); - qlk.q.lock_w(); - _locked_w = true; - } - } - void Lock::DBRead::lockTop() { - switch (_lockState->threadState()) { - case 'r': - case 'w': - break; - default: - verify(false); - case 0 : - verify(_lockState->threadState() == 0); - _lockState->lockedStart('r'); - qlk.q.lock_r(); - _locked_r = true; - } - } - - void Lock::DBRead::lockOther(const StringData& db) { - fassert( 16255, !db.empty() ); - - // we do checks first, as on assert destructor won't be called so don't want to be half finished with our work. - if( _lockState->otherCount() ) { - // nested. prev could be read or write. if/when we do temprelease with DBRead/DBWrite we will need to increment/decrement here - // (so we can not release or assert if nested). temprelease we should avoid if we can though, it's a bit of an anti-pattern. - invariant(db == _lockState->otherName()); - return; - } - - // first lock for this db. check consistent order with local db lock so we never deadlock. local always comes last - invariant(_lockState->nestableCount() == 0); - - if (db != _lockState->otherName()) { - DBLocksMap::ref r(dblocks); - WrapperForRWLock*& lock = r[db]; - if (lock == NULL) { - lock = new WrapperForRWLock(db); - } - - _lockState->lockedOther(db, -1, lock); - } - else { - DEV OCCASIONALLY{ dassert(dblocks.get(db) == _lockState->otherLock()); } - _lockState->lockedOther(-1); - } - - fassert(16135,_weLocked==0); - _lockState->otherLock()->lock_shared(); - _weLocked = _lockState->otherLock(); - } - - Lock::UpgradeGlobalLockToExclusive::UpgradeGlobalLockToExclusive(LockState* lockState) - : _lockState(lockState) { - fassert( 16187, _lockState->threadState() == 'w' ); - - // We're about to temporarily drop w, so stop the lock time stopwatch - _lockState->recordLockTime(); - - _gotUpgrade = qlk.w_to_X(); - if ( _gotUpgrade ) { - _lockState->changeLockState('W'); - _lockState->resetLockTime(); - } - } - - Lock::UpgradeGlobalLockToExclusive::~UpgradeGlobalLockToExclusive() { - if ( _gotUpgrade ) { - fassert(16188, _lockState->threadState() == 'W'); - _lockState->recordLockTime(); - qlk.X_to_w(); - _lockState->changeLockState('w'); - } - else { - fassert(16189, _lockState->threadState() == 'w'); - } - - // Start recording lock time again - _lockState->resetLockTime(); - } - - writelocktry::writelocktry(LockState* lockState, int tryms) : - _got( false ), - _dbwlock( NULL ) - { - try { - _dbwlock.reset(new Lock::GlobalWrite(lockState, tryms)); - } - catch ( DBTryLockTimeoutException & ) { - return; - } - _got = true; - } - - writelocktry::~writelocktry() { - - } - - // note: the 'already' concept here might be a bad idea as a temprelease wouldn't notice it is nested then - readlocktry::readlocktry(LockState* lockState, int tryms) : - _got( false ), - _dbrlock( NULL ) - { - try { - _dbrlock.reset(new Lock::GlobalRead(lockState, tryms)); - } - catch ( DBTryLockTimeoutException & ) { - return; - } - _got = true; - } - - readlocktry::~readlocktry() { - - } - - /** - * This is passed to the iterator for global environments and aggregates information about the - * locks which are currently being held or waited on. - */ - class LockStateAggregator : public GlobalEnvironmentExperiment::ProcessOperationContext { - public: - LockStateAggregator(bool blockedOnly) - : numWriteLocked(0), - numReadLocked(0), - _blockedOnly(blockedOnly) { - - } - - virtual void processOpContext(OperationContext* txn) { - if (_blockedOnly && !txn->lockState()->hasLockPending()) { - return; - } - - if (txn->lockState()->isWriteLocked()) { - numWriteLocked++; - } - else { - numReadLocked++; - } - } - - int numWriteLocked; - int numReadLocked; - - private: - const bool _blockedOnly; - }; - - - class GlobalLockServerStatusSection : public ServerStatusSection { - public: - GlobalLockServerStatusSection() : ServerStatusSection( "globalLock" ){ - _started = curTimeMillis64(); - } - - virtual bool includeByDefault() const { return true; } - - virtual BSONObj generateSection( const BSONElement& configElement ) const { - BSONObjBuilder t; - - t.append( "totalTime" , (long long)(1000 * ( curTimeMillis64() - _started ) ) ); - t.append( "lockTime" , Lock::globalLockStat()->getTimeLocked( 'W' ) ); - - // This returns the blocked lock states - { - BSONObjBuilder ttt( t.subobjStart( "currentQueue" ) ); - - LockStateAggregator blocked(true); - getGlobalEnvironment()->forEachOperationContext(&blocked); - - ttt.append("total", blocked.numReadLocked + blocked.numWriteLocked); - ttt.append("readers", blocked.numReadLocked); - ttt.append("writers", blocked.numWriteLocked); - ttt.done(); - } - - // This returns all the active clients (including those holding locks) - { - BSONObjBuilder ttt( t.subobjStart( "activeClients" ) ); - - LockStateAggregator active(false); - getGlobalEnvironment()->forEachOperationContext(&active); - - ttt.append("total", active.numReadLocked + active.numWriteLocked); - ttt.append("readers", active.numReadLocked); - ttt.append("writers", active.numWriteLocked); - ttt.done(); - } - - return t.obj(); - } - - private: - unsigned long long _started; - - } globalLockServerStatusSection; - - class LockStatsServerStatusSection : public ServerStatusSection { - public: - LockStatsServerStatusSection() : ServerStatusSection( "locks" ){} - virtual bool includeByDefault() const { return true; } - - BSONObj generateSection( const BSONElement& configElement ) const { - BSONObjBuilder b; - b.append(".", qlk.stats.report()); - b.append("admin", nestableLocks[Lock::admin]->getStats().report()); - b.append("local", nestableLocks[Lock::local]->getStats().report()); - { - DBLocksMap::ref r(dblocks); - for( DBLocksMap::const_iterator i = r.r.begin(); i != r.r.end(); ++i ) { - b.append(i->first, i->second->getStats().report()); - } - } - return b.obj(); - } - - } lockStatsServerStatusSection; -} diff --git a/src/mongo/db/d_concurrency.h b/src/mongo/db/d_concurrency.h deleted file mode 100644 index 7a93b0936ae..00000000000 --- a/src/mongo/db/d_concurrency.h +++ /dev/null @@ -1,253 +0,0 @@ -// @file d_concurrency.h - -/** -* Copyright (C) 2008-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 . -* -* 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. -*/ - - -// only used by mongod, thus the name ('d') -// (also used by dbtests test binary, which is running mongod test code) - -#pragma once - -#include "mongo/base/string_data.h" -#include "mongo/db/jsobj.h" -#include "mongo/db/lockstat.h" -#include "mongo/util/concurrency/mutex.h" -#include "mongo/util/concurrency/rwlock.h" - -namespace mongo { - - class WrapperForRWLock; - class LockState; - - class Lock : boost::noncopyable { - public: - enum Nestable { notnestable=0, local, admin }; - - static LockStat* globalLockStat(); - static LockStat* nestableLockStat( Nestable db ); - - class ScopedLock; - - // note: avoid TempRelease when possible. not a good thing. - struct TempRelease { - TempRelease(LockState* lockState); - ~TempRelease(); - const bool cant; // true if couldn't because of recursive locking - - // Not owned - LockState* _lockState; - ScopedLock *scopedLk; - }; - - /** turn on "parallel batch writer mode". blocks all other threads. this mode is off - by default. note only one thread creates a ParallelBatchWriterMode object; the rest just - call iAmABatchParticipant(). Note that this lock is not released on a temprelease, just - the normal lock things below. - */ - class ParallelBatchWriterMode : boost::noncopyable { - RWLockRecursive::Exclusive _lk; - public: - ParallelBatchWriterMode() : _lk(_batchLock) {} - static void iAmABatchParticipant(LockState* lockState); - static RWLockRecursive &_batchLock; - }; - - public: - - class ScopedLock : boost::noncopyable { - public: - virtual ~ScopedLock(); - - /** @return micros since we started acquiring */ - long long acquireFinished( LockStat* stat ); - - // Accrue elapsed lock time since last we called reset - void recordTime(); - // Start recording a new period, starting now() - void resetTime(); - - protected: - explicit ScopedLock(LockState* lockState, char type ); - - private: - friend struct TempRelease; - void tempRelease(); // TempRelease class calls these - void relock(); - - protected: - virtual void _tempRelease() = 0; - virtual void _relock() = 0; - - LockState* _lockState; - - private: - - class ParallelBatchWriterSupport : boost::noncopyable { - public: - ParallelBatchWriterSupport(LockState* lockState); - - private: - void tempRelease(); - void relock(); - - LockState* _lockState; - scoped_ptr _lk; - friend class ScopedLock; - }; - - ParallelBatchWriterSupport _pbws_lk; - - void _recordTime( long long micros ); - Timer _timer; - char _type; // 'r','w','R','W' - LockStat* _stat; // the stat for the relevant lock to increment when we're done - }; - - // note that for these classes recursive locking is ok if the recursive locking "makes sense" - // i.e. you could grab globalread after globalwrite. - - class GlobalWrite : public ScopedLock { - bool noop; - protected: - void _tempRelease(); - void _relock(); - public: - // stopGreed is removed and does NOT work - // timeoutms is only for writelocktry -- deprecated -- do not use - GlobalWrite(LockState* lockState, int timeoutms = -1); - virtual ~GlobalWrite(); - void downgrade(); // W -> R - void upgrade(); // caution see notes - }; - - class GlobalRead : public ScopedLock { // recursive is ok - public: - bool noop; - protected: - void _tempRelease(); - void _relock(); - public: - // timeoutms is only for readlocktry -- deprecated -- do not use - GlobalRead(LockState* lockState, int timeoutms = -1); - virtual ~GlobalRead(); - }; - - // lock this database. do not shared_lock globally first, that is handledin herein. - class DBWrite : public ScopedLock { - /** - * flow - * 1) lockDB - * a) lockTop - * b) lockNestable or lockOther - * 2) unlockDB - */ - - void lockTop(); - void lockNestable(Nestable db); - void lockOtherWrite(const StringData& db); - void lockOtherRead(const StringData& db); - void lockDB(const std::string& ns); - void unlockDB(); - - protected: - void _tempRelease(); - void _relock(); - - public: - DBWrite(LockState* lockState, const StringData& dbOrNs, bool intentWrite = false); - virtual ~DBWrite(); - - private: - bool _locked_w; - bool _locked_W; - bool _isIntentWrite; - WrapperForRWLock *_weLocked; - const std::string _what; - bool _nested; - }; - - // lock this database for reading. do not shared_lock globally first, that is handledin herein. - class DBRead : public ScopedLock { - void lockTop(); - void lockNestable(Nestable db); - void lockOther(const StringData& db); - void lockDB(const std::string& ns); - void unlockDB(); - - protected: - void _tempRelease(); - void _relock(); - - public: - DBRead(LockState* lockState, const StringData& dbOrNs); - virtual ~DBRead(); - - private: - bool _locked_r; - WrapperForRWLock *_weLocked; - std::string _what; - bool _nested; - - }; - - /** - * Acquires a previously acquired intent-X (lower-case 'w') GlobalWrite lock to upper-case - * 'W' lock. Effectively means "stop the world". - */ - class UpgradeGlobalLockToExclusive : private boost::noncopyable { - public: - UpgradeGlobalLockToExclusive(LockState* lockState); - ~UpgradeGlobalLockToExclusive(); - - bool gotUpgrade() const { return _gotUpgrade; } - - private: - LockState* _lockState; - bool _gotUpgrade; - }; - }; - - class readlocktry : boost::noncopyable { - bool _got; - scoped_ptr _dbrlock; - public: - readlocktry(LockState* lockState, int tryms); - ~readlocktry(); - bool got() const { return _got; } - }; - - class writelocktry : boost::noncopyable { - bool _got; - scoped_ptr _dbwlock; - public: - writelocktry(LockState* lockState, int tryms); - ~writelocktry(); - bool got() const { return _got; } - }; -} diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 45b3dff01c2..9fc531a5e7c 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -51,7 +51,7 @@ #include "mongo/db/clientcursor.h" #include "mongo/db/commands/server_status.h" #include "mongo/db/commands/server_status_metric.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/d_globals.h" #include "mongo/db/db.h" #include "mongo/db/dbmessage.h" diff --git a/src/mongo/db/index_builder.cpp b/src/mongo/db/index_builder.cpp index 14b153f098f..5398c5f3b5c 100644 --- a/src/mongo/db/index_builder.cpp +++ b/src/mongo/db/index_builder.cpp @@ -36,7 +36,7 @@ #include "mongo/db/curop.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/repl/rs.h" #include "mongo/db/operation_context_impl.h" #include "mongo/util/log.h" diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index 6c097791b8c..a7073da73e7 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -43,7 +43,7 @@ #include "mongo/db/background.h" #include "mongo/db/clientcursor.h" #include "mongo/db/commands/fsync.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/db.h" #include "mongo/db/dbhelpers.h" #include "mongo/db/dbmessage.h" diff --git a/src/mongo/db/lockstat.cpp b/src/mongo/db/lockstat.cpp deleted file mode 100644 index 30e3b901792..00000000000 --- a/src/mongo/db/lockstat.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// lockstat.cpp - -/** -* Copyright (C) 2008 10gen 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 . -* -* 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/pch.h" - -#include "mongo/db/lockstat.h" - -#include "mongo/db/jsobj.h" - -namespace mongo { - - BSONObj LockStat::report() const { - BSONObjBuilder b; - - BSONObjBuilder t( b.subobjStart( "timeLockedMicros" ) ); - _append( b, timeLocked, timeAcquiring ); - t.done(); - - BSONObjBuilder a( b.subobjStart( "timeAcquiringMicros" ) ); - _append( a, timeAcquiring, timeLocked ); - a.done(); - - return b.obj(); - } - - void LockStat::report( StringBuilder& builder ) const { - bool prefixPrinted = false; - for ( int i=0; i < N; i++ ) { - if ( timeLocked[i].load() == 0 ) - continue; - - if ( ! prefixPrinted ) { - builder << "locks(micros)"; - prefixPrinted = true; - } - - builder << ' ' << nameFor( i ) << ':' << timeLocked[i].load(); - } - - } - - void LockStat::_append( BSONObjBuilder& builder, - const AtomicInt64* data, - const AtomicInt64* additionalIndicator ) { - - if ( data[0].load() || - data[1].load() || - ( additionalIndicator && ( additionalIndicator[0].load() || - additionalIndicator[1].load() ) ) ) { - builder.append( "R" , data[0].load() ); - builder.append( "W" , data[1].load() ); - } - - if ( data[2].load() || - data[3].load() || - ( additionalIndicator && ( additionalIndicator[2].load() || - additionalIndicator[3].load() ) ) ) { - builder.append( "r" , data[2].load() ); - builder.append( "w" , data[3].load() ); - } - } - - unsigned LockStat::mapNo(char type) { - switch( type ) { - case 'R' : return 0; - case 'W' : return 1; - case 'r' : return 2; - case 'w' : return 3; - default: ; - } - fassert(16146,false); - return 0; - } - - char LockStat::nameFor(unsigned offset) { - switch ( offset ) { - case 0: return 'R'; - case 1: return 'W'; - case 2: return 'r'; - case 3: return 'w'; - } - fassertFailed(16339); - } - - - void LockStat::recordAcquireTimeMicros( char type , long long micros ) { - timeAcquiring[mapNo(type)].fetchAndAdd( micros ); - } - void LockStat::recordLockTimeMicros( char type , long long micros ) { - timeLocked[mapNo(type)].fetchAndAdd( micros ); - } - - void LockStat::reset() { - for ( int i = 0; i < N; i++ ) { - timeAcquiring[i].store(0); - timeLocked[i].store(0); - } - } -} diff --git a/src/mongo/db/lockstat.h b/src/mongo/db/lockstat.h deleted file mode 100644 index 82b24e3c004..00000000000 --- a/src/mongo/db/lockstat.h +++ /dev/null @@ -1,68 +0,0 @@ -// lockstat.h - -/** -* Copyright (C) 2008 10gen 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 . -* -* 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 "mongo/bson/util/builder.h" -#include "mongo/platform/atomic_word.h" -#include "mongo/util/timer.h" - -namespace mongo { - - class BSONObj; - - class LockStat { - enum { N = 4 }; - public: - void recordAcquireTimeMicros( char type , long long micros ); - void recordLockTimeMicros( char type , long long micros ); - - void reset(); - - BSONObj report() const; - void report( StringBuilder& builder ) const; - - long long getTimeLocked( char type ) const { return timeLocked[mapNo(type)].load(); } - private: - static void _append( BSONObjBuilder& builder, - const AtomicInt64* data, - const AtomicInt64* additionalIndicator ); - - // RWrw - // in micros - AtomicInt64 timeAcquiring[N]; - AtomicInt64 timeLocked[N]; - - static unsigned mapNo(char type); - static char nameFor(unsigned offset); - }; - -} diff --git a/src/mongo/db/lockstate.cpp b/src/mongo/db/lockstate.cpp deleted file mode 100644 index 3e5744703f7..00000000000 --- a/src/mongo/db/lockstate.cpp +++ /dev/null @@ -1,311 +0,0 @@ -// lockstate.cpp - -/** -* Copyright (C) 2008 10gen 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 . -* -* 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/db/lockstate.h" - -#include "mongo/db/namespace_string.h" -#include "mongo/util/log.h" -#include "mongo/util/mongoutils/str.h" - - -namespace mongo { - - LockState::LockState() - : _batchWriter(false), - _recursive(0), - _threadState(0), - _whichNestable( Lock::notnestable ), - _nestableCount(0), - _otherCount(0), - _otherLock(NULL), - _scopedLk(NULL), - _lockPending(false), - _lockPendingParallelWriter(false) - { - } - - bool LockState::isRW() const { - return _threadState == 'R' || _threadState == 'W'; - } - - bool LockState::isW() const { - return _threadState == 'W'; - } - - bool LockState::hasAnyReadLock() const { - return _threadState == 'r' || _threadState == 'R'; - } - - bool LockState::isLocked( const StringData& ns ) const { - char db[MaxDatabaseNameLen]; - nsToDatabase(ns, db); - - DEV verify( _otherName.find( '.' ) == string::npos ); // XXX this shouldn't be here, but somewhere - if ( _otherCount && db == _otherName ) - return true; - - if ( _nestableCount ) { - if ( mongoutils::str::equals( db , "local" ) ) - return _whichNestable == Lock::local; - if ( mongoutils::str::equals( db , "admin" ) ) - return _whichNestable == Lock::admin; - } - - return false; - } - - bool LockState::isLocked() const { - return threadState() != 0; - } - - bool LockState::isWriteLocked() const { - return (threadState() == 'W' || threadState() == 'w'); - } - - bool LockState::isWriteLocked(const StringData& ns) const { - if (isWriteLocked()) { - return true; - } - - return isLocked(ns); - } - - bool LockState::isAtLeastReadLocked(const StringData& ns) const { - if (threadState() == 'R' || threadState() == 'W') - return true; // global - if (threadState() == 0) - return false; - return isLocked(ns); - } - - bool LockState::isLockedForCommitting() const { - return threadState() == 'R' || threadState() == 'W'; - } - - bool LockState::isRecursive() const { - return recursiveCount() > 1; - } - - void LockState::assertWriteLocked(const StringData& ns) const { - if (!isWriteLocked(ns)) { - dump(); - msgasserted( - 16105, mongoutils::str::stream() << "expected to be write locked for " << ns); - } - } - - void LockState::assertAtLeastReadLocked(const StringData& ns) const { - if (!isAtLeastReadLocked(ns)) { - log() << "error expected " << ns << " to be locked " << endl; - dump(); - msgasserted( - 16104, mongoutils::str::stream() << "expected to be read locked for " << ns); - } - } - - void LockState::lockedStart( char newState ) { - _threadState = newState; - } - void LockState::unlocked() { - _threadState = 0; - } - - void LockState::changeLockState( char newState ) { - fassert( 16169 , _threadState != 0 ); - _threadState = newState; - } - - static string kind(int n) { - if( n > 0 ) - return "W"; - if( n < 0 ) - return "R"; - return "?"; - } - - BSONObj LockState::reportState() { - BSONObjBuilder b; - reportState(&b); - - return b.obj(); - } - - /** Note: this is is called by the currentOp command, which is a different - thread. So be careful about thread safety here. For example reading - this->otherName would not be safe as-is! - */ - void LockState::reportState(BSONObjBuilder* res) { - BSONObjBuilder b; - if( _threadState ) { - char buf[2]; - buf[0] = _threadState; - buf[1] = 0; - b.append("^", buf); - } - if( _nestableCount ) { - string s = "?"; - if( _whichNestable == Lock::local ) - s = "^local"; - else if( _whichNestable == Lock::admin ) - s = "^admin"; - b.append(s, kind(_nestableCount)); - } - if( _otherCount ) { - WrapperForRWLock *k = _otherLock; - if( k ) { - string s = "^"; - s += k->name(); - b.append(s, kind(_otherCount)); - } - } - BSONObj o = b.obj(); - if (!o.isEmpty()) { - res->append("locks", o); - } - res->append("waitingForLock", _lockPending); - } - - void LockState::dump() const { - char s = _threadState; - stringstream ss; - ss << "lock status: "; - if( s == 0 ){ - ss << "unlocked"; - } - else { - ss << s; - if( _recursive ) { - ss << " recursive:" << _recursive; - } - ss << " otherCount:" << _otherCount; - if( _otherCount ) { - ss << " otherdb:" << _otherName; - } - if( _nestableCount ) { - ss << " nestableCount:" << _nestableCount << " which:"; - if( _whichNestable == Lock::local ) - ss << "local"; - else if( _whichNestable == Lock::admin ) - ss << "admin"; - else - ss << (int)_whichNestable; - } - } - log() << ss.str() << endl; - } - - void LockState::enterScopedLock( Lock::ScopedLock* lock ) { - _recursive++; - if ( _recursive == 1 ) { - fassert(16115, _scopedLk == 0); - _scopedLk = lock; - } - } - - Lock::ScopedLock* LockState::leaveScopedLock() { - _recursive--; - dassert( _recursive < 10000 ); - Lock::ScopedLock* temp = _scopedLk; - - if ( _recursive > 0 ) { - return NULL; - } - - _scopedLk = NULL; - return temp; - } - - void LockState::lockedNestable( Lock::Nestable what , int type) { - verify( type ); - _whichNestable = what; - _nestableCount += type; - } - - void LockState::unlockedNestable() { - _whichNestable = Lock::notnestable; - _nestableCount = 0; - } - - void LockState::lockedOther( int type ) { - fassert( 16231 , _otherCount == 0 ); - _otherCount = type; - } - - void LockState::lockedOther( const StringData& other , int type , WrapperForRWLock* lock ) { - fassert( 16170 , _otherCount == 0 ); - _otherName = other.toString(); - _otherCount = type; - _otherLock = lock; - } - - void LockState::unlockedOther() { - // we leave _otherName and _otherLock set as - // _otherLock exists to cache a pointer - _otherCount = 0; - } - - LockStat* LockState::getRelevantLockStat() { - if ( _whichNestable ) - return Lock::nestableLockStat( _whichNestable ); - - if ( _otherCount && _otherLock ) - return &_otherLock->getStats(); - - if ( isRW() ) - return Lock::globalLockStat(); - - return 0; - } - - - Acquiring::Acquiring( Lock::ScopedLock* lock, LockState& ls ) - : _lock( lock ), _ls( ls ){ - _ls._lockPending = true; - } - - Acquiring::~Acquiring() { - _ls._lockPending = false; - LockStat* stat = _ls.getRelevantLockStat(); - if ( stat && _lock ) { - // increment the global stats for this counter - stat->recordAcquireTimeMicros( _ls.threadState(), _lock->acquireFinished( stat ) ); - } - } - - AcquiringParallelWriter::AcquiringParallelWriter( LockState& ls ) - : _ls( ls ) { - _ls._lockPendingParallelWriter = true; - } - - AcquiringParallelWriter::~AcquiringParallelWriter() { - _ls._lockPendingParallelWriter = false; - } - -} diff --git a/src/mongo/db/lockstate.h b/src/mongo/db/lockstate.h deleted file mode 100644 index 7f484c592a6..00000000000 --- a/src/mongo/db/lockstate.h +++ /dev/null @@ -1,177 +0,0 @@ -// lockstate.h - -/** -* Copyright (C) 2008 10gen 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 . -* -* 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 "mongo/db/d_concurrency.h" - -namespace mongo { - - class Acquiring; - - // per thread - class LockState { - public: - LockState(); - - void dump() const; - - BSONObj reportState(); - void reportState(BSONObjBuilder* b); - - unsigned recursiveCount() const { return _recursive; } - - /** - * @return 0 rwRW - */ - char threadState() const { return _threadState; } - - bool isRW() const; // RW - bool isW() const; // W - bool hasAnyReadLock() const; // explicitly rR - - bool isLocked(const StringData& ns) const; // rwRW - bool isLocked() const; - bool isWriteLocked() const; - bool isWriteLocked(const StringData& ns) const; - bool isAtLeastReadLocked(const StringData& ns) const; - bool isLockedForCommitting() const; - bool isRecursive() const; - - void assertWriteLocked(const StringData& ns) const; - void assertAtLeastReadLocked(const StringData& ns) const; - - /** pending means we are currently trying to get a lock */ - bool hasLockPending() const { return _lockPending || _lockPendingParallelWriter; } - - // ---- - - - void lockedStart( char newState ); // RWrw - void unlocked(); // _threadState = 0 - - /** - * you have to be locked already to call this - * this is mostly for W_to_R or R_to_W - */ - void changeLockState( char newstate ); - - Lock::Nestable whichNestable() const { return _whichNestable; } - int nestableCount() const { return _nestableCount; } - - int otherCount() const { return _otherCount; } - const std::string& otherName() const { return _otherName; } - WrapperForRWLock* otherLock() const { return _otherLock; } - - void enterScopedLock( Lock::ScopedLock* lock ); - Lock::ScopedLock* leaveScopedLock(); - - void lockedNestable( Lock::Nestable what , int type ); - void unlockedNestable(); - void lockedOther( const StringData& db , int type , WrapperForRWLock* lock ); - void lockedOther( int type ); // "same lock as last time" case - void unlockedOther(); - bool _batchWriter; - - LockStat* getRelevantLockStat(); - void recordLockTime() { _scopedLk->recordTime(); } - void resetLockTime() { _scopedLk->resetTime(); } - - private: - unsigned _recursive; // we allow recursively asking for a lock; we track that here - - // global lock related - char _threadState; // 0, 'r', 'w', 'R', 'W' - - // db level locking related - Lock::Nestable _whichNestable; - int _nestableCount; // recursive lock count on local or admin db XXX - change name - - int _otherCount; // >0 means write lock, <0 read lock - XXX change name - std::string _otherName; // which database are we locking and working with (besides local/admin) - WrapperForRWLock* _otherLock; // so we don't have to check the map too often (the map has a mutex) - - // for temprelease - // for the nonrecursive case. otherwise there would be many - // the first lock goes here, which is ok since we can't yield recursive locks - Lock::ScopedLock* _scopedLk; - - bool _lockPending; - bool _lockPendingParallelWriter; - - friend class Acquiring; - friend class AcquiringParallelWriter; - }; - - class WrapperForRWLock : boost::noncopyable { - SimpleRWLock rw; - SimpleMutex m; - bool sharedLatching; - LockStat stats; - public: - std::string name() const { return rw.name; } - LockStat& getStats() { return stats; } - - WrapperForRWLock(const StringData& name) - : rw(name), m(name) { - // For the local datbase, all operations are short, - // either writing one entry, or doing a tail. - // In tests, use a SimpleMutex is much faster for the local db. - sharedLatching = name != "local"; - } - void lock() { if ( sharedLatching ) { rw.lock(); } else { m.lock(); } } - void lock_shared() { if ( sharedLatching ) { rw.lock_shared(); } else { m.lock(); } } - void unlock() { if ( sharedLatching ) { rw.unlock(); } else { m.unlock(); } } - void unlock_shared() { if ( sharedLatching ) { rw.unlock_shared(); } else { m.unlock(); } } - }; - - class ScopedLock; - - class Acquiring { - public: - Acquiring( Lock::ScopedLock* lock, LockState& ls ); - ~Acquiring(); - private: - Lock::ScopedLock* _lock; - LockState& _ls; - }; - - class AcquiringParallelWriter { - public: - AcquiringParallelWriter( LockState& ls ); - ~AcquiringParallelWriter(); - - private: - LockState& _ls; - }; - - -} diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index 50b0d7736f8..8ca67393a7d 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -34,8 +34,8 @@ #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/db/storage/recovery_unit.h" -#include "mongo/db/lockstate.h" #include "mongo/db/concurrency/lock_mgr.h" +#include "mongo/db/concurrency/lock_state.h" namespace mongo { diff --git a/src/mongo/db/repl/network_interface_impl.cpp b/src/mongo/db/repl/network_interface_impl.cpp index 57cf6c4a51a..845c63c0abb 100644 --- a/src/mongo/db/repl/network_interface_impl.cpp +++ b/src/mongo/db/repl/network_interface_impl.cpp @@ -31,7 +31,7 @@ #include "mongo/db/repl/network_interface_impl.h" #include "mongo/client/connpool.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/operation_context_impl.h" #include "mongo/util/assert_util.h" diff --git a/src/mongo/db/repl/rs_sync.cpp b/src/mongo/db/repl/rs_sync.cpp index 82ec24381a9..e68edeb1a54 100644 --- a/src/mongo/db/repl/rs_sync.cpp +++ b/src/mongo/db/repl/rs_sync.cpp @@ -40,7 +40,7 @@ #include "mongo/db/commands/fsync.h" #include "mongo/db/commands/server_status.h" #include "mongo/db/curop.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/namespace_string.h" #include "mongo/db/prefetch.h" #include "mongo/db/repl/bgsync.h" diff --git a/src/mongo/db/stats/snapshots_webplugins.cpp b/src/mongo/db/stats/snapshots_webplugins.cpp index 176ccf24ac5..e01065d71f1 100644 --- a/src/mongo/db/stats/snapshots_webplugins.cpp +++ b/src/mongo/db/stats/snapshots_webplugins.cpp @@ -28,7 +28,7 @@ #include "mongo/platform/basic.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/dbwebserver.h" #include "mongo/db/operation_context.h" #include "mongo/db/stats/snapshots.h" diff --git a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp index 795082b26b5..eb72d376e19 100644 --- a/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp +++ b/src/mongo/db/storage/mmap_v1/catalog/namespace_index.cpp @@ -35,7 +35,7 @@ #include -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/operation_context.h" #include "mongo/db/storage/mmap_v1/catalog/namespace_details.h" #include "mongo/util/exit.h" diff --git a/src/mongo/db/storage/mmap_v1/data_file.cpp b/src/mongo/db/storage/mmap_v1/data_file.cpp index 521589c6c95..f4426d27b49 100644 --- a/src/mongo/db/storage/mmap_v1/data_file.cpp +++ b/src/mongo/db/storage/mmap_v1/data_file.cpp @@ -30,15 +30,14 @@ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage -#include "mongo/pch.h" +#include "mongo/platform/basic.h" #include "mongo/db/storage/mmap_v1/data_file.h" #include -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage/mmap_v1/dur.h" -#include "mongo/db/lockstate.h" #include "mongo/db/operation_context.h" #include "mongo/util/file_allocator.h" #include "mongo/util/log.h" diff --git a/src/mongo/db/storage/mmap_v1/dur_commitjob.h b/src/mongo/db/storage/mmap_v1/dur_commitjob.h index 70100499bf1..89a97a5ba3c 100644 --- a/src/mongo/db/storage/mmap_v1/dur_commitjob.h +++ b/src/mongo/db/storage/mmap_v1/dur_commitjob.h @@ -30,7 +30,7 @@ #pragma once -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/storage/mmap_v1/durop.h" #include "mongo/util/alignedbuilder.h" diff --git a/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp b/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp index b9d6231f8fa..3f23be7188d 100644 --- a/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp +++ b/src/mongo/db/storage/mmap_v1/durable_mapped_file.cpp @@ -39,7 +39,7 @@ #include "mongo/db/storage/mmap_v1/durable_mapped_file.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage_options.h" #include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/storage/mmap_v1/dur_journalformat.h" diff --git a/src/mongo/db/storage/mmap_v1/durop.cpp b/src/mongo/db/storage/mmap_v1/durop.cpp index 494cd4bca5a..29192362e91 100644 --- a/src/mongo/db/storage/mmap_v1/durop.cpp +++ b/src/mongo/db/storage/mmap_v1/durop.cpp @@ -34,7 +34,7 @@ #include "mongo/db/storage/mmap_v1/durop.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage/mmap_v1/durable_mapped_file.h" #include "mongo/db/storage/mmap_v1/mmap_v1_engine.h" #include "mongo/util/alignedbuilder.h" diff --git a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp index 3a09d6d4eab..9749bc0bfea 100644 --- a/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp +++ b/src/mongo/db/storage/mmap_v1/mmap_v1_extent_manager.cpp @@ -36,7 +36,7 @@ #include "mongo/db/audit.h" #include "mongo/db/client.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage/mmap_v1/dur.h" #include "mongo/db/storage/mmap_v1/data_file.h" #include "mongo/db/storage/mmap_v1/record.h" diff --git a/src/mongo/dbtests/commandtests.cpp b/src/mongo/dbtests/commandtests.cpp index 955620eac94..364a73fcf84 100644 --- a/src/mongo/dbtests/commandtests.cpp +++ b/src/mongo/dbtests/commandtests.cpp @@ -28,7 +28,7 @@ #include "mongo/platform/basic.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/operation_context_impl.h" #include "mongo/dbtests/dbtests.h" diff --git a/src/mongo/dbtests/threadedtests.cpp b/src/mongo/dbtests/threadedtests.cpp index 678678bbb87..425409d3848 100644 --- a/src/mongo/dbtests/threadedtests.cpp +++ b/src/mongo/dbtests/threadedtests.cpp @@ -35,7 +35,7 @@ #include -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/operation_context_impl.h" #include "mongo/dbtests/dbtests.h" #include "mongo/platform/atomic_word.h" diff --git a/src/mongo/s/d_merge.cpp b/src/mongo/s/d_merge.cpp index 6a4cb65dfdf..861d932f5c3 100644 --- a/src/mongo/s/d_merge.cpp +++ b/src/mongo/s/d_merge.cpp @@ -27,7 +27,7 @@ */ #include "mongo/base/owned_pointer_vector.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/s/d_logic.h" diff --git a/src/mongo/s/s_only.cpp b/src/mongo/s/s_only.cpp index f925d13ce84..ed5aa2568e2 100644 --- a/src/mongo/s/s_only.cpp +++ b/src/mongo/s/s_only.cpp @@ -63,8 +63,6 @@ namespace mongo { TSP_DEFINE(Client,currentClient) - LockState::LockState(){} // ugh - Client::Client(const string& desc, AbstractMessagingPort *p) : ClientBasic(p), _shutdown(false), diff --git a/src/mongo/util/mmap_posix.cpp b/src/mongo/util/mmap_posix.cpp index 3e0b2d89a5a..52c64249f50 100644 --- a/src/mongo/util/mmap_posix.cpp +++ b/src/mongo/util/mmap_posix.cpp @@ -36,7 +36,7 @@ #include #include "mongo/platform/atomic_word.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/util/file_allocator.h" #include "mongo/util/log.h" #include "mongo/util/mmap.h" diff --git a/src/mongo/util/mmap_win.cpp b/src/mongo/util/mmap_win.cpp index 9865784dc0a..19479cd3452 100644 --- a/src/mongo/util/mmap_win.cpp +++ b/src/mongo/util/mmap_win.cpp @@ -29,7 +29,7 @@ #include "mongo/pch.h" -#include "mongo/db/d_concurrency.h" +#include "mongo/db/concurrency/d_concurrency.h" #include "mongo/db/storage/mmap_v1/durable_mapped_file.h" #include "mongo/util/file_allocator.h" #include "mongo/util/log.h" -- cgit v1.2.1