diff --git a/include/crucible/multilock.h b/include/crucible/multilock.h new file mode 100644 index 0000000..6b7fb6c --- /dev/null +++ b/include/crucible/multilock.h @@ -0,0 +1,40 @@ +#ifndef CRUCIBLE_MULTILOCK_H +#define CRUCIBLE_MULTILOCK_H + +#include +#include +#include +#include +#include + +namespace crucible { + using namespace std; + + class MultiLocker { + mutex m_mutex; + condition_variable m_cv; + map m_counters; + + class LockHandle { + const string m_type; + MultiLocker &m_parent; + bool m_locked = false; + void set_locked(bool state); + public: + ~LockHandle(); + LockHandle(const string &type, MultiLocker &parent); + friend class MultiLocker; + }; + + friend class LockHandle; + + bool is_lock_available(const string &type); + void put_lock(const string &type); + shared_ptr get_lock_private(const string &type); + public: + static shared_ptr get_lock(const string &type); + }; + +} + +#endif // CRUCIBLE_MULTILOCK_H diff --git a/lib/Makefile b/lib/Makefile index 622abf9..16557a2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,6 +13,7 @@ CRUCIBLE_OBJS = \ extentwalker.o \ fd.o \ fs.o \ + multilock.o \ ntoa.o \ path.o \ process.o \ diff --git a/lib/multilock.cc b/lib/multilock.cc new file mode 100644 index 0000000..8a56271 --- /dev/null +++ b/lib/multilock.cc @@ -0,0 +1,72 @@ +#include "crucible/multilock.h" + +#include "crucible/error.h" + +namespace crucible { + using namespace std; + + MultiLocker::LockHandle::LockHandle(const string &type, MultiLocker &parent) : + m_type(type), + m_parent(parent) + { + } + + void + MultiLocker::LockHandle::set_locked(const bool state) + { + m_locked = state; + } + + MultiLocker::LockHandle::~LockHandle() + { + if (m_locked) { + m_parent.put_lock(m_type); + m_locked = false; + } + } + + bool + MultiLocker::is_lock_available(const string &type) + { + for (const auto &i : m_counters) { + if (i.second != 0 && i.first != type) { + return false; + } + } + return true; + } + + void + MultiLocker::put_lock(const string &type) + { + unique_lock lock(m_mutex); + auto &counter = m_counters[type]; + THROW_CHECK2(runtime_error, type, counter, counter > 0); + --counter; + if (counter == 0) { + m_cv.notify_all(); + } + } + + shared_ptr + MultiLocker::get_lock_private(const string &type) + { + unique_lock lock(m_mutex); + m_counters.insert(make_pair(type, size_t(0))); + while (!is_lock_available(type)) { + m_cv.wait(lock); + } + const auto rv = make_shared(type, *this); + ++m_counters[type]; + rv->set_locked(true); + return rv; + } + + shared_ptr + MultiLocker::get_lock(const string &type) + { + static MultiLocker s_process_instance; + return s_process_instance.get_lock_private(type); + } + +}