// @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 . * * 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/util/concurrency/mutex.h" #include "mongo/util/concurrency/rwlockimpl.h" #include "mongo/util/concurrency/simplerwlock.h" #include "mongo/util/debug_util.h" #include "mongo/util/time_support.h" namespace mongo { 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(); } void unlock() { 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 verify( x == UpgradableState ); RWLockBase::upgrade(); x = Exclusive; } bool lock_shared_try( int millis ) { return RWLockBase::lock_shared_try(millis); } bool lock_try( int millis = 0 ) { return RWLockBase::lock_try(millis); } /** 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 { MONGO_DISALLOW_COPYING(Upgradable); RWLock& _r; public: Upgradable(RWLock& r) : _r(r) { r.lockAsUpgradable(); verify( _r.x == NilState ); _r.x = RWLock::UpgradableState; } ~Upgradable() { if( _r.x == RWLock::UpgradableState ) { _r.x = NilState; _r.unlockFromUpgradable(); } else { //TEMP verify( _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 { MONGO_DISALLOW_COPYING(rwlock_try_write); 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 { MONGO_DISALLOW_COPYING(rwlock_shared); public: rwlock_shared(RWLock& rwlock) : _r(rwlock) {_r.lock_shared(); } ~rwlock_shared() { _r.unlock_shared(); } private: RWLock& _r; }; /* scoped lock for RWLock */ class rwlock { MONGO_DISALLOW_COPYING(rwlock); 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 _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 assertAtLeastReadLocked() { verify( _state.get() != 0 ); } void assertExclusivelyLocked() { verify( _state.get() < 0 ); } class Exclusive { MONGO_DISALLOW_COPYING(Exclusive); 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 { MONGO_DISALLOW_COPYING(Shared); 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 / 20 ) ) sleep = 10; sleepmillis(sleep); i += ( sleep - 1 ); } if ( ! got ) { 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); } }; }