#ifndef CRUCIBLE_LOCKSET_H #define CRUCIBLE_LOCKSET_H #include #include #include #include #include #include namespace crucible { using namespace std; template class LockSet { public: using key_type = T; using set_type = set; private: set_type m_set; mutex m_mutex; condition_variable m_condvar; public: ~LockSet(); LockSet() = default; void lock(const key_type &name); void unlock(const key_type &name); bool try_lock(const key_type &name); size_t size(); bool empty(); set_type copy(); void wait_unlock(double interval); class Lock { LockSet &m_lockset; key_type m_name; bool m_locked; Lock() = delete; Lock(const Lock &) = delete; Lock& operator=(const Lock &) = delete; public: ~Lock(); Lock(LockSet &lockset, const key_type &m_name, bool start_locked = true); Lock(Lock &&that); Lock& operator=(Lock &&that); void lock(); void unlock(); bool try_lock(); }; }; template LockSet::~LockSet() { if (!m_set.empty()) { cerr << "ERROR: " << m_set.size() << " locked items still in set at destruction" << endl; } // We will crash later. Might as well crash now. assert(m_set.empty()); } template void LockSet::lock(const key_type &name) { unique_lock lock(m_mutex); while (m_set.count(name)) { m_condvar.wait(lock); } auto rv = m_set.insert(name); THROW_CHECK0(runtime_error, rv.second); } template bool LockSet::try_lock(const key_type &name) { unique_lock lock(m_mutex); if (m_set.count(name)) { return false; } auto rv = m_set.insert(name); THROW_CHECK1(runtime_error, name, rv.second); return true; } template void LockSet::unlock(const key_type &name) { unique_lock lock(m_mutex); m_condvar.notify_all(); auto erase_count = m_set.erase(name); THROW_CHECK1(invalid_argument, erase_count, erase_count == 1); } template void LockSet::wait_unlock(double interval) { unique_lock lock(m_mutex); if (m_set.empty()) return; m_condvar.wait_for(lock, chrono::duration(interval)); } template size_t LockSet::size() { unique_lock lock(m_mutex); return m_set.size(); } template bool LockSet::empty() { unique_lock lock(m_mutex); return m_set.empty(); } template typename LockSet::set_type LockSet::copy() { unique_lock lock(m_mutex); return m_set; } template void LockSet::Lock::lock() { if (m_locked) return; m_lockset.lock(m_name); m_locked = true; } template bool LockSet::Lock::try_lock() { if (m_locked) return true; m_locked = m_lockset.try_lock(m_name); return m_locked; } template void LockSet::Lock::unlock() { if (!m_locked) return; m_lockset.unlock(m_name); m_locked = false; } template LockSet::Lock::~Lock() { if (m_locked) { unlock(); } } template LockSet::Lock::Lock(LockSet &lockset, const key_type &name, bool start_locked) : m_lockset(lockset), m_name(name), m_locked(false) { if (start_locked) { lock(); } } template LockSet::Lock::Lock(Lock &&that) : m_lockset(that.lockset), m_name(that.m_name), m_locked(that.m_locked) { that.m_locked = false; } template typename LockSet::Lock & LockSet::Lock::operator=(Lock &&that) { THROW_CHECK2(invalid_argument, &m_lockset, &that.m_lockset, &m_lockset == &that.m_lockset); if (m_locked && that.m_name != m_name) { unlock(); } m_name = that.m_name; m_locked = that.m_locked; that.m_locked = false; return *this; } } #endif // CRUCIBLE_LOCKSET_H