about summary refs log tree commit diff
path: root/src/libutil
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-23T14·00+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-23T14·00+0100
commite292144d46e3fbb24ee9ee67f1682b268373921b (patch)
tree318c8baaca0739076dcbcbe0d530f5e8d97fc204 /src/libutil
parentc0b7a8a0b576d5fcbcb25c412836dc885b7eb0fe (diff)
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.
Diffstat (limited to 'src/libutil')
-rw-r--r--src/libutil/pool.hh102
-rw-r--r--src/libutil/sync.hh78
2 files changed, 180 insertions, 0 deletions
diff --git a/src/libutil/pool.hh b/src/libutil/pool.hh
new file mode 100644
index 000000000000..d63912e28aa9
--- /dev/null
+++ b/src/libutil/pool.hh
@@ -0,0 +1,102 @@
+#pragma once
+
+#include <memory>
+#include <list>
+#include <functional>
+
+#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<Connection> 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 R>
+class Pool
+{
+public:
+
+    typedef std::function<ref<R>()> Factory;
+
+private:
+
+    Factory factory;
+
+    struct State
+    {
+        unsigned int count = 0;
+        std::list<ref<R>> idle;
+    };
+
+    Sync<State> state;
+
+public:
+
+    Pool(const Factory & factory = []() { return make_ref<R>(); })
+        : factory(factory)
+    { }
+
+    class Handle
+    {
+    private:
+        Pool & pool;
+        ref<R> r;
+
+        friend Pool;
+
+        Handle(Pool & pool, std::shared_ptr<R> 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 000000000000..3abffa7c7437
--- /dev/null
+++ b/src/libutil/sync.hh
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <mutex>
+#include <condition_variable>
+#include <cassert>
+
+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> data;
+
+     {
+       auto data_(data.lock());
+       data_->x = 123;
+     }
+
+   Here, "data" is automatically unlocked when "data_" goes out of
+   scope.
+*/
+
+template<class T>
+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<class Rep, class Period, class Predicate>
+        bool wait_for(std::condition_variable_any & cv,
+            const std::chrono::duration<Rep, Period> & duration,
+            Predicate pred)
+        {
+            assert(s);
+            return cv.wait_for(s->mutex, duration, pred);
+        }
+
+        template<class Clock, class Duration>
+        std::cv_status wait_until(std::condition_variable_any & cv,
+            const std::chrono::time_point<Clock, Duration> & duration)
+        {
+            assert(s);
+            return cv.wait_until(s->mutex, duration);
+        }
+    };
+
+    Lock lock() { return Lock(this); }
+};
+
+}