// @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 . */ #pragma once #include "mutex.h" #include "../time_support.h" // this requires Vista+ to work // it works better than sharable_mutex under high contention //#define MONGO_USE_SRW_ON_WINDOWS 1 #if !defined(MONGO_USE_SRW_ON_WINDOWS) #if BOOST_VERSION >= 103500 # define BOOST_RWLOCK #else # if defined(_WIN32) # error need boost >= 1.35 for windows # endif # include #endif #if defined(_WIN32) # include "shared_mutex_win.hpp" namespace mongo { typedef boost::modified_shared_mutex shared_mutex; } # undef assert # define assert MONGO_assert #elif defined(BOOST_RWLOCK) # include # undef assert # define assert MONGO_assert #endif #endif namespace mongo { #if defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32) class RWLock { public: RWLock(const char *) { InitializeSRWLock(&_lock); } ~RWLock() { } void lock() { AcquireSRWLockExclusive(&_lock); } void unlock() { ReleaseSRWLockExclusive(&_lock); } void lock_shared() { AcquireSRWLockShared(&_lock); } void unlock_shared() { ReleaseSRWLockShared(&_lock); } bool lock_shared_try( int millis ) { unsigned long long end = curTimeMicros64() + millis*1000; while( 1 ) { if( TryAcquireSRWLockShared(&_lock) ) return true; if( curTimeMicros64() >= end ) break; Sleep(1); } return false; } bool lock_try( int millis = 0 ) { unsigned long long end = curTimeMicros64() + millis*1000; while( 1 ) { if( TryAcquireSRWLockExclusive(&_lock) ) return true; if( curTimeMicros64() >= end ) break; Sleep(1); } return false; } private: SRWLOCK _lock; }; #elif defined(BOOST_RWLOCK) class RWLock { shared_mutex _m; public: #if defined(_DEBUG) const char *_name; RWLock(const char *name) : _name(name) { } #else RWLock(const char *) { } #endif void lock() { _m.lock(); #if defined(_DEBUG) mutexDebugger.entering(_name); #endif } void unlock() { #if defined(_DEBUG) mutexDebugger.leaving(_name); #endif _m.unlock(); } void lock_shared() { _m.lock_shared(); } void unlock_shared() { _m.unlock_shared(); } bool lock_shared_try( int millis ) { boost::system_time until = get_system_time(); until += boost::posix_time::milliseconds(millis); if( _m.timed_lock_shared( until ) ) { return true; } return false; } bool lock_try( int millis = 0 ) { boost::system_time until = get_system_time(); until += boost::posix_time::milliseconds(millis); if( _m.timed_lock( until ) ) { #if defined(_DEBUG) mutexDebugger.entering(_name); #endif return true; } return false; } }; #else class RWLock { pthread_rwlock_t _lock; inline void check( int x ) { if( x == 0 ) return; log() << "pthread rwlock failed: " << x << endl; assert( x == 0 ); } public: #if defined(_DEBUG) const char *_name; RWLock(const char *name) : _name(name) { #else RWLock(const char *) { #endif check( pthread_rwlock_init( &_lock , 0 ) ); } ~RWLock() { if ( ! StaticObserver::_destroyingStatics ) { check( pthread_rwlock_destroy( &_lock ) ); } } void lock() { check( pthread_rwlock_wrlock( &_lock ) ); #if defined(_DEBUG) mutexDebugger.entering(_name); #endif } void unlock() { #if defined(_DEBUG) mutexDebugger.leaving(_name); #endif check( pthread_rwlock_unlock( &_lock ) ); } void lock_shared() { check( pthread_rwlock_rdlock( &_lock ) ); } void unlock_shared() { check( pthread_rwlock_unlock( &_lock ) ); } bool lock_shared_try( int millis ) { return _try( millis , false ); } bool lock_try( int millis = 0 ) { if( _try( millis , true ) ) { #if defined(_DEBUG) mutexDebugger.entering(_name); #endif return true; } return false; } bool _try( int millis , bool write ) { while ( true ) { int x = write ? pthread_rwlock_trywrlock( &_lock ) : pthread_rwlock_tryrdlock( &_lock ); if ( x <= 0 ) { return true; } if ( millis-- <= 0 ) return false; if ( x == EBUSY ) { sleepmillis(1); continue; } check(x); } return false; } }; #endif /** throws on failure to acquire in the specified time period. */ class 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; }; /* scoped lock for RWLock */ class rwlock { public: rwlock( const RWLock& lock , bool write , bool alreadyHaveLock = false ) : _lock( (RWLock&)lock ) , _write( write ) { if ( ! alreadyHaveLock ) { if ( _write ) _lock.lock(); else _lock.lock_shared(); } } ~rwlock() { if ( _write ) _lock.unlock(); else _lock.unlock_shared(); } private: RWLock& _lock; const bool _write; }; }