// rwlock.h /* * 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" #if BOOST_VERSION >= 103500 #define BOOST_RWLOCK #else #if defined(_WIN32) #error need boost >= 1.35 for windows #endif #include #endif #ifdef BOOST_RWLOCK #include #undef assert #define assert MONGO_assert #endif namespace mongo { #ifdef BOOST_RWLOCK class RWLock { boost::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 ( ! __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 class rwlock_try_write { RWLock& _l; 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(); } }; /* scoped lock */ struct rwlock { 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(); } RWLock& _lock; bool _write; }; }