mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
multilocker: serialize conflicting parallel operations
For performance or workaround reasons we sometimes have to avoid doing two conflicting operations at the same time, but we can still run any number of non-conflicting operations in parallel. MultiLocker (suggestions for a better class name welcome) blocks the calling thread until there are no threads attempting to run a conflicting operation. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
cc87125e41
commit
4a4a2de89f
40
include/crucible/multilock.h
Normal file
40
include/crucible/multilock.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef CRUCIBLE_MULTILOCK_H
|
||||
#define CRUCIBLE_MULTILOCK_H
|
||||
|
||||
#include <condition_variable>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace crucible {
|
||||
using namespace std;
|
||||
|
||||
class MultiLocker {
|
||||
mutex m_mutex;
|
||||
condition_variable m_cv;
|
||||
map<string, size_t> 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<LockHandle> get_lock_private(const string &type);
|
||||
public:
|
||||
static shared_ptr<LockHandle> get_lock(const string &type);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CRUCIBLE_MULTILOCK_H
|
@ -13,6 +13,7 @@ CRUCIBLE_OBJS = \
|
||||
extentwalker.o \
|
||||
fd.o \
|
||||
fs.o \
|
||||
multilock.o \
|
||||
ntoa.o \
|
||||
path.o \
|
||||
process.o \
|
||||
|
72
lib/multilock.cc
Normal file
72
lib/multilock.cc
Normal file
@ -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<mutex> 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::LockHandle>
|
||||
MultiLocker::get_lock_private(const string &type)
|
||||
{
|
||||
unique_lock<mutex> 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<LockHandle>(type, *this);
|
||||
++m_counters[type];
|
||||
rv->set_locked(true);
|
||||
return rv;
|
||||
}
|
||||
|
||||
shared_ptr<MultiLocker::LockHandle>
|
||||
MultiLocker::get_lock(const string &type)
|
||||
{
|
||||
static MultiLocker s_process_instance;
|
||||
return s_process_instance.get_lock_private(type);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user