stlab 2.4.0
Modern, modular C++ algorithms, data structures, and concurrency primitives
Loading...
Searching...
No Matches
await.hpp
Go to the documentation of this file.
1/*
2 Copyright 2015 Adobe
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5*/
6
7/**************************************************************************************************/
8
9#ifndef STLAB_CONCURRENCY_AWAIT_HPP
10#define STLAB_CONCURRENCY_AWAIT_HPP
11
23
24#include <stlab/config.hpp>
25
26#include <chrono>
27#include <condition_variable>
28#include <exception>
29#include <mutex>
30#include <utility>
31
33#include <stlab/concurrency/immediate_executor.hpp>
35#include <stlab/memory.hpp>
36
37#include <stlab/concurrency/default_executor.hpp>
38
39/**************************************************************************************************/
40
41namespace stlab {
42STLAB_VERSION_NAMESPACE_BEGIN()
43
44
49
50/**************************************************************************************************/
51
52
55template <class F>
56auto invoke_waiting(F&& f) {
57 if (!stlab_v2_default_executor_supports_blocking()) std::terminate();
58 stlab_v2_notify_default_executor_before_waiting();
59
60 return std::forward<F>(f)();
61}
62
63/**************************************************************************************************/
64
69
70template <class T>
71auto await(future<T>&& x) -> T {
72 if (x.is_ready()) return std::move(x).get_ready(); // if ready, done
73 if (!stlab_v2_default_executor_supports_blocking()) std::terminate();
74
75 std::mutex m;
76 std::condition_variable condition;
77 future<T> result;
78
79 auto hold = std::move(x).recover(immediate_executor, [&](future<T>&& r) {
80 {
81 std::unique_lock<std::mutex> lock{m};
82 result = std::move(r);
83 condition.notify_one(); // must notify under lock
84 }
85 });
86 std::unique_lock<std::mutex> lock{m};
87 invoke_waiting([&] { condition.wait(lock, [&] { return result.is_ready(); }); });
88 return std::move(result).get_ready();
89}
90
92
93template <class T>
94[[deprecated("implicit copy deprecated, use `await(std::move(f))` or `await(stlab::copy(f))`"
95 " instead.")]]
96auto await(const future<T>& x) -> T {
97 return await(future<T>{x});
98}
99
100namespace detail {
101
102template <class T>
103struct blocking_get_guarded {
104 bool _timed_out{false};
105 future<T> _result;
106 std::condition_variable _condition;
107 std::mutex _mutex;
108
109 template <class F>
110 auto set(F&& r) -> future<T> {
111 {
112 std::unique_lock<std::mutex> lock{_mutex};
113 if (_timed_out) return std::forward<F>(r);
114 _result = std::forward<F>(r);
115 }
116 _condition.notify_one();
117 /*
118 A valid future<> result is required because the following reduction in
119 recover() will attach a continuation - which will crash on an invalid
120 future<>.
121 */
123 std::make_exception_ptr(std::runtime_error("not an error")), immediate_executor);
124 }
125
126 auto wait_for(const std::chrono::nanoseconds& timeout) -> future<T> {
127 std::unique_lock<std::mutex> lock{_mutex};
128 _timed_out = !invoke_waiting(
129 [&] { return _condition.wait_for(lock, timeout, [&] { return _result.valid(); }); });
130 return _timed_out ? future<T>{} : std::move(_result);
131 }
132};
133
134} // namespace detail
135
139template <class T>
140auto await_for(future<T>&& x, const std::chrono::nanoseconds& timeout) -> future<T> {
141 if (x.is_ready() || !stlab_v2_default_executor_supports_blocking()) return std::move(x);
142
143 auto p = std::make_shared<detail::blocking_get_guarded<T>>();
144
145 auto hold = std::move(x).recover(immediate_executor, [_p = stlab::make_weak_ptr(p)](auto&& r) {
146 if (auto p = _p.lock()) return p->set(std::forward<decltype(r)>(r));
147 return std::forward<decltype(r)>(r);
148 });
149
150 auto result = p->wait_for(timeout);
151 return result.valid() ? std::move(result) : std::move(hold);
152}
153
155
156template <class T>
157[[deprecated("implicit copy deprecated, use `await_for(std::move(f), t)` or"
158 " `await_for(stlab::copy(f), t)` instead.")]]
159auto await_for(const future<T>& x, const std::chrono::nanoseconds& timeout) -> future<T> {
160 return await_for(future<T>{x}, timeout);
161}
162
163/**************************************************************************************************/
164
166
167STLAB_VERSION_NAMESPACE_END()
168} // namespace stlab
169
170/**************************************************************************************************/
171
172#endif // STLAB_CONCURRENCY_AWAIT_HPP
One-shot asynchronous result: holds a value or exception produced by a promise or packaged_task.
Definition future.hpp:306
Futures, packaged tasks, and coroutine integration.
auto await(future< T > &&x) -> T
Synchronously wait for the result x. If x resolves as an exception, the exception is rethrown....
Definition await.hpp:71
auto invoke_waiting(F &&f)
Assumes f will block waiting; on the portable task system, wakes the pool or adds a worker (up to the...
Definition await.hpp:56
auto await_for(future< T > &&x, const std::chrono::nanoseconds &timeout) -> future< T >
Waits at most timeout, returning a future that retains any pending result. In cooperative threadless ...
Definition await.hpp:140
auto make_exceptional_future(const std::exception_ptr &error, E executor) -> future< T >
Creates a future already failed with error (executor runs attached continuations).
Definition ready_future.hpp:86
auto make_weak_ptr(const std::shared_ptr< T > &x)
Returns a std::weak_ptr<T> sharing ownership with x.
Definition memory.hpp:35
Small memory-related utilities (make_weak_ptr).
Definition reverse.hpp:28
Factory functions for already-resolved futures.