stlab 2.3.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
35#include <stlab/memory.hpp>
36
37#if STLAB_TASK_SYSTEM(PORTABLE)
39#endif
40
41/**************************************************************************************************/
42
43namespace stlab {
44STLAB_VERSION_NAMESPACE_BEGIN()
45
46
51
52/**************************************************************************************************/
53
54
56template <class F>
57auto invoke_waiting(F&& f) {
58#if STLAB_TASK_SYSTEM(PORTABLE)
59 if (!detail::pts().wake()) detail::pts().add_thread();
60#endif
61
62 return std::forward<F>(f)();
63}
64
65/**************************************************************************************************/
66
70
71template <class T>
72auto await(future<T>&& x) -> T {
73 if (x.is_ready()) return std::move(x).get_ready(); // if ready, done
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
136template <class T>
137auto await_for(future<T>&& x, const std::chrono::nanoseconds& timeout) -> future<T> {
138 if (x.is_ready()) return std::move(x);
139
140 auto p = std::make_shared<detail::blocking_get_guarded<T>>();
141
142 auto hold = std::move(x).recover(immediate_executor, [_p = stlab::make_weak_ptr(p)](auto&& r) {
143 if (auto p = _p.lock()) return p->set(std::forward<decltype(r)>(r));
144 return std::forward<decltype(r)>(r);
145 });
146
147 auto result = p->wait_for(timeout);
148 return result.valid() ? std::move(result) : std::move(hold);
149}
150
152
153template <class T>
154[[deprecated("implicit copy deprecated, use `await_for(std::move(f), t)` or"
155 " `await_for(stlab::copy(f), t)` instead.")]]
156auto await_for(const future<T>& x, const std::chrono::nanoseconds& timeout) -> future<T> {
157 return await_for(future<T>{x}, timeout);
158}
159
160/**************************************************************************************************/
161
168template <class T>
169[[deprecated("Use await instead.")]]
171 return await(std::move(x));
172}
173
175template <class T>
176[[deprecated("Use await_for instead.")]]
177auto blocking_get_for(future<T> x, const std::chrono::nanoseconds& timeout) -> future<T> {
178 return await_for(std::move(x), timeout);
179}
180
182template <class T>
183[[deprecated("Use await_for instead.")]]
184auto blocking_get(future<T> x, const std::chrono::nanoseconds& timeout) -> decltype(x.get_try()) {
185 return blocking_get_for(std::move(x), timeout).get_try();
186}
187
188/**************************************************************************************************/
189
191
192STLAB_VERSION_NAMESPACE_END()
193} // namespace stlab
194
195/**************************************************************************************************/
196
197#endif // STLAB_CONCURRENCY_AWAIT_HPP
One-shot asynchronous result: holds a value or exception produced by a promise or packaged_task.
Definition future.hpp:300
Thread-pool executors mapping to the OS scheduler (libdispatch, Windows pool, portable).
Futures, packaged tasks, channels, 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:72
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:57
auto blocking_get(future< T > x) -> T
Definition await.hpp:170
auto blocking_get_for(future< T > x, const std::chrono::nanoseconds &timeout) -> future< T >
Definition await.hpp:177
constexpr auto immediate_executor
Invokes work inline on the calling thread (synchronous executor).
Definition immediate_executor.hpp:52
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
constexpr auto move(T &&t) noexcept -> std::remove_reference_t< T > &&
A standard move implementation but with a compile-time check for const types.
Definition utility.hpp:154
Synchronous inline executor.
Small memory-related utilities (make_weak_ptr).
Definition reverse.hpp:28
Factory functions for already-resolved futures.