// Copyright (C) 2010 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_READ_WRITE_MUTEX_EXTENSIOn_ #define DLIB_READ_WRITE_MUTEX_EXTENSIOn_ #include "threads_kernel.h" #include "read_write_mutex_extension_abstract.h" namespace dlib { // ---------------------------------------------------------------------------------------- class read_write_mutex { /*! INITIAL VALUE - max_locks == defined by constructor - available_locks == max_locks - write_lock_in_progress == false - write_lock_active == false CONVENTION - Each time someone gets a read only lock they take one of the "available locks" and each write lock takes all possible locks (i.e. max_locks). The number of available locks is recorded in available_locks. Any time you try to lock this object and there aren't available locks you have to wait. - max_locks == max_readonly_locks() - if (some thread is on the process of obtaining a write lock) then - write_lock_in_progress == true - else - write_lock_in_progress == false - if (some thread currently has a write lock on this mutex) then - write_lock_active == true - else - write_lock_active == false !*/ public: read_write_mutex ( ) : s(m), max_locks(0xFFFFFFFF), available_locks(max_locks), write_lock_in_progress(false), write_lock_active(false) {} explicit read_write_mutex ( unsigned long max_locks_ ) : s(m), max_locks(max_locks_), available_locks(max_locks_), write_lock_in_progress(false), write_lock_active(false) { // make sure requires clause is not broken DLIB_ASSERT(max_locks > 0, "\t read_write_mutex::read_write_mutex(max_locks)" << "\n\t You must give a non-zero value for max_locks" << "\n\t this: " << this ); } ~read_write_mutex ( ) {} void lock ( ) const { m.lock(); // If another write lock is already in progress then wait for it to finish // before we start trying to grab all the available locks. This way we // don't end up fighting over the locks. while (write_lock_in_progress) s.wait(); // grab the right to perform a write lock write_lock_in_progress = true; // now start grabbing all the locks unsigned long locks_obtained = available_locks; available_locks = 0; while (locks_obtained != max_locks) { s.wait(); locks_obtained += available_locks; available_locks = 0; } write_lock_in_progress = false; write_lock_active = true; m.unlock(); } void unlock ( ) const { m.lock(); // only do something if there really was a lock in place if (write_lock_active) { available_locks = max_locks; write_lock_active = false; s.broadcast(); } m.unlock(); } void lock_readonly ( ) const { m.lock(); while (available_locks == 0) s.wait(); --available_locks; m.unlock(); } void unlock_readonly ( ) const { m.lock(); // If this condition is false then it means there are no more readonly locks // to free. So we don't do anything. if (available_locks != max_locks && !write_lock_active) { ++available_locks; // only perform broadcast when there is another thread that might be listening if (available_locks == 1 || write_lock_in_progress) { s.broadcast(); } } m.unlock(); } unsigned long max_readonly_locks ( ) const { return max_locks; } private: mutex m; signaler s; const unsigned long max_locks; mutable unsigned long available_locks; mutable bool write_lock_in_progress; mutable bool write_lock_active; // restricted functions read_write_mutex(read_write_mutex&); // copy constructor read_write_mutex& operator=(read_write_mutex&); // assignment operator }; // ---------------------------------------------------------------------------------------- } #endif // DLIB_READ_WRITE_MUTEX_EXTENSIOn_