diff --git a/include/crucible/lockset.h b/include/crucible/lockset.h index e792e9d..76b4135 100644 --- a/include/crucible/lockset.h +++ b/include/crucible/lockset.h @@ -1,6 +1,7 @@ #ifndef CRUCIBLE_LOCKSET_H #define CRUCIBLE_LOCKSET_H +#include #include #include @@ -12,6 +13,8 @@ #include #include #include +#include +#include namespace crucible { using namespace std; @@ -29,8 +32,11 @@ namespace crucible { mutex m_mutex; condition_variable m_condvar; size_t m_max_size = numeric_limits::max(); + set m_priorities; + uint64_t m_priority_counter; bool full(); + bool first_in_priority(uint64_t my_priority); bool locked(const key_type &name); class Lock { @@ -96,6 +102,26 @@ namespace crucible { return m_set.size() >= m_max_size; } + template + bool + LockSet::first_in_priority(uint64_t my_priority) + { +#if 1 + auto counter = m_max_size; + for (auto i : m_priorities) { + if (i == my_priority) { + return true; + } + if (++counter > m_max_size) { + return false; + } + } + THROW_ERROR(runtime_error, "my_priority " << my_priority << " not in m_priorities (size " << m_priorities.size() << ")"); +#else + return *m_priorities.begin() == my_priority; +#endif + } + template bool LockSet::locked(const key_type &name) @@ -105,9 +131,10 @@ namespace crucible { template void - LockSet::max_size(size_t s) + LockSet::max_size(size_t new_max_size) { - m_max_size = s; + THROW_CHECK1(out_of_range, new_max_size, new_max_size > 0); + m_max_size = new_max_size; } template @@ -115,11 +142,18 @@ namespace crucible { LockSet::lock(const key_type &name) { unique_lock lock(m_mutex); - while (full() || locked(name)) { + auto my_priority = m_priority_counter++; + Cleanup cleanup([&]() { + m_priorities.erase(my_priority); + }); + m_priorities.insert(my_priority); + while (full() || locked(name) || !first_in_priority(my_priority)) { m_condvar.wait(lock); } auto rv = m_set.insert(make_pair(name, gettid())); THROW_CHECK0(runtime_error, rv.second); + // We removed our priority slot so other threads have to check again + m_condvar.notify_all(); } template @@ -142,6 +176,8 @@ namespace crucible { unique_lock lock(m_mutex); auto erase_count = m_set.erase(name); m_condvar.notify_all(); + lock.unlock(); + this_thread::yield(); THROW_CHECK1(invalid_argument, erase_count, erase_count == 1); }