From e292144d46e3fbb24ee9ee67f1682b268373921b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Feb 2016 15:00:59 +0100 Subject: RemoteStore: Make thread-safe This allows a RemoteStore object to be used safely from multiple threads concurrently. It will make multiple daemon connections if necessary. Note: pool.hh and sync.hh have been copied from the Hydra source tree. --- src/libutil/pool.hh | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/libutil/sync.hh | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 src/libutil/pool.hh create mode 100644 src/libutil/sync.hh (limited to 'src/libutil') diff --git a/src/libutil/pool.hh b/src/libutil/pool.hh new file mode 100644 index 0000000000..d63912e28a --- /dev/null +++ b/src/libutil/pool.hh @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#include "sync.hh" +#include "ref.hh" + +namespace nix { + +/* This template class implements a simple pool manager of resources + of some type R, such as database connections. It is used as + follows: + + class Connection { ... }; + + Pool pool; + + { + auto conn(pool.get()); + conn->exec("select ..."); + } + + Here, the Connection object referenced by ‘conn’ is automatically + returned to the pool when ‘conn’ goes out of scope. +*/ + +template +class Pool +{ +public: + + typedef std::function()> Factory; + +private: + + Factory factory; + + struct State + { + unsigned int count = 0; + std::list> idle; + }; + + Sync state; + +public: + + Pool(const Factory & factory = []() { return make_ref(); }) + : factory(factory) + { } + + class Handle + { + private: + Pool & pool; + ref r; + + friend Pool; + + Handle(Pool & pool, std::shared_ptr r) : pool(pool), r(r) { } + + public: + Handle(Handle && h) : pool(h.pool), r(h.r) { abort(); } + + Handle(const Handle & l) = delete; + + ~Handle() + { + auto state_(pool.state.lock()); + state_->idle.push_back(r); + } + + R * operator -> () { return &*r; } + R & operator * () { return *r; } + }; + + Handle get() + { + { + auto state_(state.lock()); + if (!state_->idle.empty()) { + auto p = state_->idle.back(); + state_->idle.pop_back(); + return Handle(*this, p); + } + state_->count++; + } + /* Note: we don't hold the lock while creating a new instance, + because creation might take a long time. */ + return Handle(*this, factory()); + } + + unsigned int count() + { + auto state_(state.lock()); + return state_->count; + } +}; + +} diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh new file mode 100644 index 0000000000..3abffa7c74 --- /dev/null +++ b/src/libutil/sync.hh @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +namespace nix { + +/* This template class ensures synchronized access to a value of type + T. It is used as follows: + + struct Data { int x; ... }; + + Sync data; + + { + auto data_(data.lock()); + data_->x = 123; + } + + Here, "data" is automatically unlocked when "data_" goes out of + scope. +*/ + +template +class Sync +{ +private: + std::mutex mutex; + T data; + +public: + + Sync() { } + Sync(const T & data) : data(data) { } + + class Lock + { + private: + Sync * s; + friend Sync; + Lock(Sync * s) : s(s) { s->mutex.lock(); } + public: + Lock(Lock && l) : s(l.s) { l.s = 0; } + Lock(const Lock & l) = delete; + ~Lock() { if (s) s->mutex.unlock(); } + T * operator -> () { return &s->data; } + T & operator * () { return s->data; } + + /* FIXME: performance impact of condition_variable_any? */ + void wait(std::condition_variable_any & cv) + { + assert(s); + cv.wait(s->mutex); + } + + template + bool wait_for(std::condition_variable_any & cv, + const std::chrono::duration & duration, + Predicate pred) + { + assert(s); + return cv.wait_for(s->mutex, duration, pred); + } + + template + std::cv_status wait_until(std::condition_variable_any & cv, + const std::chrono::time_point & duration) + { + assert(s); + return cv.wait_until(s->mutex, duration); + } + }; + + Lock lock() { return Lock(this); } +}; + +} -- cgit 1.4.1