diff options
Diffstat (limited to 'src/mongo/util/concurrency/rwlock.h')
-rw-r--r-- | src/mongo/util/concurrency/rwlock.h | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/src/mongo/util/concurrency/rwlock.h b/src/mongo/util/concurrency/rwlock.h new file mode 100644 index 00000000000..3dbfc35ed6e --- /dev/null +++ b/src/mongo/util/concurrency/rwlock.h @@ -0,0 +1,271 @@ +// @file rwlock.h generic reader-writer lock (cross platform support) + +/* + * Copyright (C) 2010 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 <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "mutex.h" +#include "../time_support.h" +#include "rwlockimpl.h" + +#if defined(_DEBUG) +#include "mutexdebugger.h" +#endif + +namespace mongo { + + /** separated out as later the implementation of this may be different than RWLock, + depending on OS, as there is no upgrade etc. facility herein. + */ + class SimpleRWLock : public RWLockBase { + public: + void lock() { RWLockBase::lock(); } + void unlock() { RWLockBase::unlock(); } + void lock_shared() { RWLockBase::lock_shared(); } + void unlock_shared() { RWLockBase::unlock_shared(); } + class Shared : boost::noncopyable { + SimpleRWLock& _r; + public: + Shared(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock_shared(); } + ~Shared() { _r.unlock_shared(); } + }; + class Exclusive : boost::noncopyable { + SimpleRWLock& _r; + public: + Exclusive(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock(); } + ~Exclusive() { _r.unlock(); } + }; + }; + + class RWLock : public RWLockBase { + enum { NilState, UpgradableState, Exclusive } x; // only bother to set when doing upgradable related things + public: + const char * const _name; + RWLock(const char *name) : _name(name) { + x = NilState; + } + void lock() { + RWLockBase::lock(); +#if defined(_DEBUG) + mutexDebugger.entering(_name); +#endif + } + void unlock() { +#if defined(_DEBUG) + mutexDebugger.leaving(_name); +#endif + RWLockBase::unlock(); + } + + void lock_shared() { RWLockBase::lock_shared(); } + void unlock_shared() { RWLockBase::unlock_shared(); } + private: + void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); } + void unlockFromUpgradable() { // upgradable -> unlocked + RWLockBase::unlockFromUpgradable(); + } + public: + void upgrade() { // upgradable -> exclusive lock + assert( x == UpgradableState ); + RWLockBase::upgrade(); + x = Exclusive; + } + + bool lock_shared_try( int millis ) { return RWLockBase::lock_shared_try(millis); } + + bool lock_try( int millis = 0 ) { + if( RWLockBase::lock_try(millis) ) { +#if defined(_DEBUG) + mutexDebugger.entering(_name); +#endif + return true; + } + return false; + } + + /** acquire upgradable state. You must be unlocked before creating. + unlocks on destruction, whether in upgradable state or upgraded to exclusive + in the interim. + */ + class Upgradable : boost::noncopyable { + RWLock& _r; + public: + Upgradable(RWLock& r) : _r(r) { + r.lockAsUpgradable(); + assert( _r.x == NilState ); + _r.x = RWLock::UpgradableState; + } + ~Upgradable() { + if( _r.x == RWLock::UpgradableState ) { + _r.x = NilState; + _r.unlockFromUpgradable(); + } + else { + //TEMP assert( _r.x == Exclusive ); // has been upgraded + _r.x = NilState; + _r.unlock(); + } + } + }; + }; + + /** throws on failure to acquire in the specified time period. */ + class rwlock_try_write : boost::noncopyable { + public: + struct exception { }; + rwlock_try_write(RWLock& l, int millis = 0) : _l(l) { + if( !l.lock_try(millis) ) + throw exception(); + } + ~rwlock_try_write() { _l.unlock(); } + private: + RWLock& _l; + }; + + class rwlock_shared : boost::noncopyable { + public: + rwlock_shared(RWLock& rwlock) : _r(rwlock) {_r.lock_shared(); } + ~rwlock_shared() { _r.unlock_shared(); } + private: + RWLock& _r; + }; + + /* scoped lock for RWLock */ + class rwlock : boost::noncopyable { + public: + /** + * @param write acquire write lock if true sharable if false + * @param lowPriority if > 0, will try to get the lock non-greedily for that many ms + */ + rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock = false , */int lowPriorityWaitMS = 0 ) + : _lock( (RWLock&)lock ) , _write( write ) { + { + if ( _write ) { + _lock.lock(); + } + else { + _lock.lock_shared(); + } + } + } + ~rwlock() { + if ( _write ) + _lock.unlock(); + else + _lock.unlock_shared(); + } + private: + RWLock& _lock; + const bool _write; + }; + + // ---------------------------------------------------------------------------------------- + + /** recursive on shared locks is ok for this implementation */ + class RWLockRecursive : protected RWLockBase { + protected: + ThreadLocalValue<int> _state; + void lock(); // not implemented - Lock() should be used; didn't overload this name to avoid mistakes + virtual void Lock() { RWLockBase::lock(); } + public: + virtual ~RWLockRecursive() { } + const char * const _name; + RWLockRecursive(const char *name) : _name(name) { } + + void assertExclusivelyLocked() { + assert( _state.get() < 0 ); + } + + class Exclusive : boost::noncopyable { + RWLockRecursive& _r; + public: + Exclusive(RWLockRecursive& r) : _r(r) { + int s = _r._state.get(); + dassert( s <= 0 ); + if( s == 0 ) + _r.Lock(); + _r._state.set(s-1); + } + ~Exclusive() { + int s = _r._state.get(); + DEV wassert( s < 0 ); // wassert: don't throw from destructors + ++s; + _r._state.set(s); + if ( s == 0 ) + _r.unlock(); + } + }; + + class Shared : boost::noncopyable { + RWLockRecursive& _r; + bool _alreadyLockedExclusiveByUs; + public: + Shared(RWLockRecursive& r) : _r(r) { + int s = _r._state.get(); + _alreadyLockedExclusiveByUs = s < 0; + if( !_alreadyLockedExclusiveByUs ) { + dassert( s >= 0 ); // -1 would mean exclusive + if( s == 0 ) + _r.lock_shared(); + _r._state.set(s+1); + } + } + ~Shared() { + if( _alreadyLockedExclusiveByUs ) { + DEV wassert( _r._state.get() < 0 ); + } + else { + int s = _r._state.get() - 1; + DEV wassert( s >= 0 ); + _r._state.set(s); + if( s == 0 ) + _r.unlock_shared(); + } + } + }; + }; + + class RWLockRecursiveNongreedy : public RWLockRecursive { + virtual void Lock() { + bool got = false; + for ( int i=0; i<lowPriorityWaitMS; i++ ) { + if ( lock_try(0) ) { + got = true; + break; + } + int sleep = 1; + if ( i > ( lowPriorityWaitMS / 20 ) ) + sleep = 10; + sleepmillis(sleep); + i += ( sleep - 1 ); + } + if ( ! got ) { + log() << "couldn't lazily get rwlock" << endl; + RWLockBase::lock(); + } + } + + public: + const int lowPriorityWaitMS; + RWLockRecursiveNongreedy(const char *nm, int lpwaitms) : RWLockRecursive(nm), lowPriorityWaitMS(lpwaitms) { } + const char * implType() const { return RWLockRecursive::implType(); } + + //just for testing: + bool __lock_try( int millis ) { return RWLockRecursive::lock_try(millis); } + }; + +} |