aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/coro/completion.h
blob: 3a97a84662bf90b064a0e57cc623c680669c7ee9 (plain) (blame)
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include "lazy.h"
#include "detached.h"
#include "received.h"

#include <coroutine>
#include <exception>
#include <future>
#include <type_traits>

namespace vespalib::coro {

// Resume (start) the coroutine responsible for calculating the result
// and signal the receiver when it completes or fails. Note that the
// detached coroutine will own both the coroutine calculating the
// result and the receiver that is later notified of the result. The
// detached coroutine will automatically self-destroy when it returns,
// thereby also destroying the value and receiver. This is the
// fundamental building block used to adapt the asynchronous result of
// a coroutine with external code. This also closely models abstract
// execution where the coroutine represented by Lazy<T> is the
// sender. Execution parameters can be encapsulated inside Lazy<T>
// using composition (for example which executor should run the
// coroutine). The receiver in this context may be either an actual
// receiver_of<T>, a callback function accepting a Received<T> or an
// std::promise. The different cases are handled by the overloaded
// Received<T>::forward function template.

template <typename T, typename R>
Detached connect_resume(Lazy<T> value, R receiver) {
    auto&& result = co_await value.forward();
    result.forward(receiver);
}

// replace Lazy<T> with std::future<T> to be able to synchronously
// wait for its completion. Implemented in terms of connect_resume.

template <typename T>
std::future<T> make_future(Lazy<T> value) {
    std::promise<T> promise;
    auto future = promise.get_future();
    connect_resume(std::move(value), std::move(promise));
    return future;
}

/**
 * Wait for a lazy value to be calculated synchronously. Make sure the
 * thread waiting is not needed in the calculation of the value, or
 * you will end up with a deadlock.
 **/
template <typename T>
T sync_wait(Lazy<T> value) {
    return make_future(std::move(value)).get();
}

/**
 * Wait for a lazy value to be calculated asynchronously; the provided
 * callback will be called with a Received<T> when the Lazy<T> is
 * done. Both the callback itself and the Lazy<T> will be destructed
 * afterwards.
 **/
template <typename T, typename F>
void async_wait(Lazy<T> value, F &&f) {
    connect_resume(std::move(value), std::forward<F>(f));
}

}