about summary refs log tree commit diff
path: root/src/libutil/util.hh
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-09-03T10·51+0200
committerEelco Dolstra <edolstra@gmail.com>2019-09-03T11·45+0200
commit7348653ff4fc4e9b2dc24943aabdb57179b1c75a (patch)
treec730713ccb743b185578ee1b37462a7fc68b10b4 /src/libutil/util.hh
parent8c4ea7a4516c517a0dd37b446bf5c1a6b157064c (diff)
Ensure that Callback is called only once
Also, make Callback movable but uncopyable.
Diffstat (limited to 'src/libutil/util.hh')
-rw-r--r--src/libutil/util.hh19
1 files changed, 16 insertions, 3 deletions
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index b538a0b41ce8..686e81d3f893 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -445,21 +445,34 @@ string get(const T & map, const string & key, const string & def = "")
    type T or an exception. (We abuse std::future<T> to pass the value or
    exception.) */
 template<typename T>
-struct Callback
+class Callback
 {
     std::function<void(std::future<T>)> fun;
+    std::atomic_flag done = ATOMIC_FLAG_INIT;
+
+public:
 
     Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
 
-    void operator()(T && t) const
+    Callback(Callback && callback) : fun(std::move(callback.fun))
+    {
+        auto prev = callback.done.test_and_set();
+        if (prev) done.test_and_set();
+    }
+
+    void operator()(T && t)
     {
+        auto prev = done.test_and_set();
+        assert(!prev);
         std::promise<T> promise;
         promise.set_value(std::move(t));
         fun(promise.get_future());
     }
 
-    void rethrow(const std::exception_ptr & exc = std::current_exception()) const
+    void rethrow(const std::exception_ptr & exc = std::current_exception())
     {
+        auto prev = done.test_and_set();
+        assert(!prev);
         std::promise<T> promise;
         promise.set_exception(exc);
         fun(promise.get_future());