aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/callback.hh
blob: 3710d1239bc52a3cd76bd7b3ecdcf225ec2bcfe0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#pragma once
///@file

#include <future>
#include <functional>

namespace nix {

/**
 * A callback is a wrapper around a lambda that accepts a valid of
 * type T or an exception. (We abuse std::future<T> to pass the value or
 * exception.)
 */
template<typename T>
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) { }

    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) noexcept
    {
        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()) noexcept
    {
        auto prev = done.test_and_set();
        assert(!prev);
        std::promise<T> promise;
        promise.set_exception(exc);
        fun(promise.get_future());
    }
};

}