#ifndef __SWTLocker_h_ #define __SWTLocker_h_ #include "MiscSupport.h" ///////////////////////////////////////////////////////////////////// // SWTLocker - Single Write Thread Locker // // This class is a convenient wrapper around a mutex. This makes it // easy to write code that always acquires a mutex before accessing // the data, and releases the mutex when you are done with the data. // // SWTLocker is similar to Locker, but they implement different // strategies for using the mutex. Locker only deals with non-const // pointers and treats all threads equaly. SWTLocker assumes that // only one thread can modify the data. // // See Locker.h for examples of how to use Locker. The following // describes how SWTLocker differs from Locker. // // After creating the SWTLocker, but before attempting to modify the // data, call setWriteThread(). If you forget you will likely get // a core dump. This is not part of the constructor for a reason. // Often these types of objects are part of a bigger object that is // constructed in the main thread. Then we create a new thread and // the bigger object does most of its work in that thread. So we // often have no idea what will be the id of the thread we're // planning to use because it doesn't exist yet. // // SWTLocker::readLock() and SWTLocker::writeLock() are both similar // to Locker::lock(). The differences are listed below. // // SWTLocker::writeLock() asserts that it is running in the write // thread before acquireing the lock. // // SWTLocker::readLock() will only acquire the lock if it is not // currently running in the write thread. And it will only give you // const pointer or reference. // // In summary, these are the rules: // o You can only change the data if you hold the mutex AND you are // in the write thread. // o You can ready the data if you hold the mutex OR you are in the // write thread. ///////////////////////////////////////////////////////////////////// template< class T > class SWTLocker { private: mutable pthread_mutex_t _mutex; T _value; ThreadId *_writer; public: template< class... Args > SWTLocker(Args... args) : _value(args...), _writer(NULL) { assertFalse(pthread_mutex_init(&_mutex, NULL)); } ~SWTLocker() { assertFalse(pthread_mutex_destroy(&_mutex)); } bool inWriteThread() const { return ThreadId::current() == _writer; } // Call this before attempting any writes. You can only set the write // thread once. By default you will set the current thread to become // the write thread. void setWriteThread(ThreadId *thread = NULL) { if (!thread) thread = ThreadId::current(); assertTrue(__sync_bool_compare_and_swap(&_writer, NULL, thread)); assert(inWriteThread()); } class ReadLock { private: SWTLocker const *_locker; public: ReadLock(SWTLocker const *locker) : _locker(locker) { if (!locker->inWriteThread()) pthread_mutex_lock(&_locker->_mutex); } ReadLock(ReadLock &&other) : _locker(other._locker) { other._locker = NULL; } ~ReadLock() { if (_locker && !_locker->inWriteThread()) pthread_mutex_unlock(&_locker->_mutex); } T const *operator ->() { return &_locker->_value; } T const &operator *() { return _locker->_value; } ReadLock(ReadLock const &) = delete; void operator =(ReadLock const &) = delete; }; ReadLock readLock() const { return ReadLock(this); } template< class Action > auto readLock(Action &action) -> decltype(action(*(T const *)NULL)) { auto l = readLock(); return action(*l); } class WriteLock { private: SWTLocker *_locker; public: WriteLock(SWTLocker *locker) : _locker(locker) { assert(_locker->inWriteThread()); pthread_mutex_lock(&_locker->_mutex); } WriteLock(WriteLock &&other) : _locker(other._locker) { other._locker = NULL; } ~WriteLock() { if (_locker) pthread_mutex_unlock(&_locker->_mutex); } T *operator ->() { return &_locker->_value; } T &operator *() { return _locker->_value; } WriteLock(WriteLock const &) = delete; void operator =(WriteLock const &) = delete; }; WriteLock writeLock() { return WriteLock(this); } template< class Action > auto writeLock(Action &action) -> decltype(action(*(T *)NULL)) { auto l = writeLock(); return action(*l); } SWTLocker(SWTLocker const &) = delete; void operator =(SWTLocker const &) = delete; }; #endif