mirror of
https://github.com/Zygo/bees.git
synced 2025-05-18 05:45:45 +02:00
lockset: avoid starvation using a priority queue
Mutex locks are released and acquired unfairly, causing arbitrary delays in acquiring locks. This prevents threads from releasing subvol FD's which in turn blocks subvol deletes. Fix by implementing a priority queue in LockSet to ensure fairness. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
a3cd3ca07f
commit
8ea92202fc
@ -1,6 +1,7 @@
|
|||||||
#ifndef CRUCIBLE_LOCKSET_H
|
#ifndef CRUCIBLE_LOCKSET_H
|
||||||
#define CRUCIBLE_LOCKSET_H
|
#define CRUCIBLE_LOCKSET_H
|
||||||
|
|
||||||
|
#include <crucible/cleanup.h>
|
||||||
#include <crucible/error.h>
|
#include <crucible/error.h>
|
||||||
#include <crucible/process.h>
|
#include <crucible/process.h>
|
||||||
|
|
||||||
@ -12,6 +13,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace crucible {
|
namespace crucible {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -29,8 +32,11 @@ namespace crucible {
|
|||||||
mutex m_mutex;
|
mutex m_mutex;
|
||||||
condition_variable m_condvar;
|
condition_variable m_condvar;
|
||||||
size_t m_max_size = numeric_limits<size_t>::max();
|
size_t m_max_size = numeric_limits<size_t>::max();
|
||||||
|
set<uint64_t> m_priorities;
|
||||||
|
uint64_t m_priority_counter;
|
||||||
|
|
||||||
bool full();
|
bool full();
|
||||||
|
bool first_in_priority(uint64_t my_priority);
|
||||||
bool locked(const key_type &name);
|
bool locked(const key_type &name);
|
||||||
|
|
||||||
class Lock {
|
class Lock {
|
||||||
@ -96,6 +102,26 @@ namespace crucible {
|
|||||||
return m_set.size() >= m_max_size;
|
return m_set.size() >= m_max_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool
|
||||||
|
LockSet<T>::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 <class T>
|
template <class T>
|
||||||
bool
|
bool
|
||||||
LockSet<T>::locked(const key_type &name)
|
LockSet<T>::locked(const key_type &name)
|
||||||
@ -105,9 +131,10 @@ namespace crucible {
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void
|
void
|
||||||
LockSet<T>::max_size(size_t s)
|
LockSet<T>::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 <class T>
|
template <class T>
|
||||||
@ -115,11 +142,18 @@ namespace crucible {
|
|||||||
LockSet<T>::lock(const key_type &name)
|
LockSet<T>::lock(const key_type &name)
|
||||||
{
|
{
|
||||||
unique_lock<mutex> lock(m_mutex);
|
unique_lock<mutex> 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);
|
m_condvar.wait(lock);
|
||||||
}
|
}
|
||||||
auto rv = m_set.insert(make_pair(name, gettid()));
|
auto rv = m_set.insert(make_pair(name, gettid()));
|
||||||
THROW_CHECK0(runtime_error, rv.second);
|
THROW_CHECK0(runtime_error, rv.second);
|
||||||
|
// We removed our priority slot so other threads have to check again
|
||||||
|
m_condvar.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -142,6 +176,8 @@ namespace crucible {
|
|||||||
unique_lock<mutex> lock(m_mutex);
|
unique_lock<mutex> lock(m_mutex);
|
||||||
auto erase_count = m_set.erase(name);
|
auto erase_count = m_set.erase(name);
|
||||||
m_condvar.notify_all();
|
m_condvar.notify_all();
|
||||||
|
lock.unlock();
|
||||||
|
this_thread::yield();
|
||||||
THROW_CHECK1(invalid_argument, erase_count, erase_count == 1);
|
THROW_CHECK1(invalid_argument, erase_count, erase_count == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user