diff options
Diffstat (limited to 'db/concurrency.h')
-rw-r--r-- | db/concurrency.h | 249 |
1 files changed, 47 insertions, 202 deletions
diff --git a/db/concurrency.h b/db/concurrency.h index 9b91b0f..39cd853 100644 --- a/db/concurrency.h +++ b/db/concurrency.h @@ -1,3 +1,5 @@ +// @file concurrency.h + /* * Copyright (C) 2010 10gen Inc. * @@ -14,9 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* concurrency.h - - mongod concurrency rules & notes will be placed here. +/*mongod concurrency rules & notes will be placed here. Mutex heirarchy (1 = "leaf") name level @@ -31,19 +31,22 @@ #include "../util/concurrency/rwlock.h" #include "../util/mmap.h" +#include "../util/time_support.h" namespace mongo { string sayClientState(); bool haveClient(); - - void curopWaitingForLock( int type ); - void curopGotLock(); + + class Client; + Client* curopWaitingForLock( int type ); + void curopGotLock(Client*); /* mutex time stats */ class MutexInfo { - unsigned long long start, enter, timeLocked; // all in microseconds + unsigned long long enter, timeLocked; // microseconds int locked; + unsigned long long start; // last as we touch this least often public: MutexInfo() : timeLocked(0) , locked(0) { @@ -61,215 +64,53 @@ namespace mongo { if ( locked == 0 ) timeLocked += curTimeMicros64() - enter; } - int isLocked() const { - return locked; - } + int isLocked() const { return locked; } void getTimingInfo(unsigned long long &s, unsigned long long &tl) const { s = start; tl = timeLocked; } - unsigned long long getTimeLocked() const { - return timeLocked; - } + unsigned long long getTimeLocked() const { return timeLocked; } }; - class MongoMutex { - MutexInfo _minfo; - RWLock _m; - ThreadLocalValue<int> _state; - - /* we use a separate TLS value for releasedEarly - that is ok as - our normal/common code path, we never even touch it. - */ - ThreadLocalValue<bool> _releasedEarly; - public: - MongoMutex(const char * name) : _m(name) { } - - /** - * @return - * > 0 write lock - * = 0 no lock - * < 0 read lock - */ - int getState() { return _state.get(); } - bool isWriteLocked() { return getState() > 0; } - void assertWriteLocked() { - assert( getState() > 0 ); - DEV assert( !_releasedEarly.get() ); - } - bool atLeastReadLocked() { return _state.get() != 0; } - void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } - - bool _checkWriteLockAlready(){ - //DEV cout << "LOCK" << endl; - DEV assert( haveClient() ); - - int s = _state.get(); - if( s > 0 ) { - _state.set(s+1); - return true; - } - - massert( 10293 , (string)"internal error: locks are not upgradeable: " + sayClientState() , s == 0 ); - - return false; - } - - void lock() { - if ( _checkWriteLockAlready() ) - return; - - _state.set(1); - - curopWaitingForLock( 1 ); - _m.lock(); - curopGotLock(); - - _minfo.entered(); - - MongoFile::lockAll(); - } - - bool lock_try( int millis ) { - if ( _checkWriteLockAlready() ) - return true; - - curopWaitingForLock( 1 ); - bool got = _m.lock_try( millis ); - curopGotLock(); - - if ( got ){ - _minfo.entered(); - _state.set(1); - MongoFile::lockAll(); - } - - return got; - } - - - void unlock() { - //DEV cout << "UNLOCK" << endl; - int s = _state.get(); - if( s > 1 ) { - _state.set(s-1); - return; - } - if( s != 1 ) { - if( _releasedEarly.get() ) { - _releasedEarly.set(false); - return; - } - massert( 12599, "internal error: attempt to unlock when wasn't in a write lock", false); - } - - MongoFile::unlockAll(); - - _state.set(0); - _minfo.leaving(); - _m.unlock(); - } - - /* unlock (write lock), and when unlock() is called later, - be smart then and don't unlock it again. - */ - void releaseEarly() { - assert( getState() == 1 ); // must not be recursive - assert( !_releasedEarly.get() ); - _releasedEarly.set(true); - unlock(); - } - - void lock_shared() { - //DEV cout << " LOCKSHARED" << endl; - int s = _state.get(); - if( s ) { - if( s > 0 ) { - // already in write lock - just be recursive and stay write locked - _state.set(s+1); - return; - } - else { - // already in read lock - recurse - _state.set(s-1); - return; - } - } - _state.set(-1); - curopWaitingForLock( -1 ); - _m.lock_shared(); - curopGotLock(); - } - - bool lock_shared_try( int millis ) { - int s = _state.get(); - if ( s ){ - // we already have a lock, so no need to try - lock_shared(); - return true; - } +} - bool got = _m.lock_shared_try( millis ); - if ( got ) - _state.set(-1); - return got; - } - - void unlock_shared() { - //DEV cout << " UNLOCKSHARED" << endl; - int s = _state.get(); - if( s > 0 ) { - assert( s > 1 ); /* we must have done a lock write first to have s > 1 */ - _state.set(s-1); - return; - } - if( s < -1 ) { - _state.set(s+1); - return; - } - assert( s == -1 ); - _state.set(0); - _m.unlock_shared(); - } - - MutexInfo& info() { return _minfo; } - }; +#include "mongomutex.h" - extern MongoMutex &dbMutex; +namespace mongo { inline void dbunlocking_write() { } inline void dbunlocking_read() { } struct writelock { - writelock(const string& ns) { - dbMutex.lock(); - } - ~writelock() { + writelock() { dbMutex.lock(); } + writelock(const string& ns) { dbMutex.lock(); } + ~writelock() { DESTRUCTOR_GUARD( dbunlocking_write(); dbMutex.unlock(); ); } }; - + struct readlock { readlock(const string& ns) { dbMutex.lock_shared(); } - ~readlock() { + readlock() { dbMutex.lock_shared(); } + ~readlock() { DESTRUCTOR_GUARD( dbunlocking_read(); dbMutex.unlock_shared(); ); } - }; + }; struct readlocktry { - readlocktry( const string&ns , int tryms ){ + readlocktry( const string&ns , int tryms ) { _got = dbMutex.lock_shared_try( tryms ); } ~readlocktry() { - if ( _got ){ + if ( _got ) { dbunlocking_read(); dbMutex.unlock_shared(); } @@ -280,11 +121,11 @@ namespace mongo { }; struct writelocktry { - writelocktry( const string&ns , int tryms ){ + writelocktry( const string&ns , int tryms ) { _got = dbMutex.lock_try( tryms ); } ~writelocktry() { - if ( _got ){ + if ( _got ) { dbunlocking_read(); dbMutex.unlock(); } @@ -294,10 +135,10 @@ namespace mongo { bool _got; }; - struct readlocktryassert : public readlocktry { - readlocktryassert(const string& ns, int tryms) : - readlocktry(ns,tryms) { - uassert(13142, "timeout getting readlock", got()); + struct readlocktryassert : public readlocktry { + readlocktryassert(const string& ns, int tryms) : + readlocktry(ns,tryms) { + uassert(13142, "timeout getting readlock", got()); } }; @@ -305,12 +146,12 @@ namespace mongo { if you have a write lock, that's ok too. */ struct atleastreadlock { - atleastreadlock( const string& ns ){ + atleastreadlock( const string& ns ) { _prev = dbMutex.getState(); if ( _prev == 0 ) dbMutex.lock_shared(); } - ~atleastreadlock(){ + ~atleastreadlock() { if ( _prev == 0 ) dbMutex.unlock_shared(); } @@ -318,6 +159,9 @@ namespace mongo { int _prev; }; + /* parameterized choice of read or write locking + use readlock and writelock instead of this when statically known which you want + */ class mongolock { bool _writelock; public: @@ -328,27 +172,28 @@ namespace mongo { else dbMutex.lock_shared(); } - ~mongolock() { + ~mongolock() { DESTRUCTOR_GUARD( - if( _writelock ) { - dbunlocking_write(); - dbMutex.unlock(); - } else { - dbunlocking_read(); - dbMutex.unlock_shared(); - } + if( _writelock ) { + dbunlocking_write(); + dbMutex.unlock(); + } + else { + dbunlocking_read(); + dbMutex.unlock_shared(); + } ); } /* this unlocks, does NOT upgrade. that works for our current usage */ void releaseAndWriteLock(); }; - - /* use writelock and readlock instead */ + + /* deprecated - use writelock and readlock instead */ struct dblock : public writelock { dblock() : writelock("") { } }; - // eliminate + // eliminate this - we should just type "dbMutex.assertWriteLocked();" instead inline void assertInWriteLock() { dbMutex.assertWriteLocked(); } } |