summaryrefslogtreecommitdiff
path: root/src/mongo/util/concurrency/rwlock.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/concurrency/rwlock.h')
-rw-r--r--src/mongo/util/concurrency/rwlock.h271
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); }
+ };
+
+}