// @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/concurrency/threadlocal.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); }
};
}