userver: userver/engine/future.hpp Source File
Loading...
Searching...
No Matches
future.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/engine/future.hpp
4/// @brief @copybrief engine::Future
5
6#include <chrono>
7#include <exception>
8#include <future>
9#include <memory>
10
11#include <userver/compiler/impl/lifetime.hpp>
12#include <userver/engine/awaitable.hpp>
13#include <userver/engine/deadline.hpp>
14#include <userver/engine/future_status.hpp>
15#include <userver/engine/impl/future_state.hpp>
16#include <userver/utils/impl/internal_tag.hpp>
17
18// TODO remove extra includes
19#include <userver/utils/assert.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23namespace engine {
24
25/// @ingroup userver_concurrency
26///
27/// @brief std::promise replacement for asynchronous tasks that works in pair
28/// with engine::Future
29///
30/// engine::Promise can be used both from coroutines and from non-coroutine
31/// threads.
32///
33/// ## Example usage:
34///
35/// @snippet core/src/engine/future_test.cpp Sample engine::Future usage
36///
37/// @see @ref scripts/docs/en/userver/synchronization.md
38template <typename T>
39class Promise;
40
41/// @ingroup userver_concurrency
42///
43/// @brief std::future replacement for asynchronous tasks that works in pair
44/// with engine::Promise
45///
46/// engine::Future can only be used from coroutine threads.
47///
48/// @warning This class supports only a single concurrent awaiter. Use
49/// @ref engine::SharedTaskWithResult to await and retrieve the same result from
50/// multiple coroutines.
51///
52/// ## Example usage:
53///
54/// @snippet core/src/engine/future_test.cpp Sample engine::Future usage
55///
56/// @see @ref scripts/docs/en/userver/synchronization.md
57template <typename T>
58class Future final {
59public:
60 /// Creates an Future without a valid state.
61 Future() = default;
62
63 Future(const Future&) = delete;
64 Future(Future&&) noexcept = default;
65 Future& operator=(const Future&) = delete;
66 Future& operator=(Future&&) noexcept = default;
67
68 /// @brief Returns whether this Future holds a valid state.
69 bool valid() const noexcept;
70
71 /// @brief Returns whether the value is available.
72 /// @warning If value is set and task is being canceled or if Promise has been
73 /// destroyed without setting a value, returns true.
74 /// @throw std::future_error if Future holds no state or if the value has
75 /// already been retrieved.
76 bool is_ready() const;
77
78 /// @brief Waits for value availability and retrieves it.
79 /// @throw WaitInterruptedException if the current task has been cancelled in
80 /// the process.
81 /// @throw std::future_error if Future holds no state, if the Promise has
82 /// been destroyed without setting a value or if the value has already been
83 /// retrieved.
84 T get();
85
86 /// @brief Waits for value availability.
87 /// @returns `FutureStatus::kReady` if the value is available.
88 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
89 /// @throw std::future_error if Future holds no state.
90 [[nodiscard]] FutureStatus wait() const;
91
92 /// @brief Waits for value availability until the timeout expires or until the
93 /// task is cancelled.
94 /// @returns `FutureStatus::kReady` if the value is available.
95 /// @returns `FutureStatus::kTimeout` if `timeout` has expired.
96 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
97 /// @throw std::future_error if Future holds no state.
98 template <typename Rep, typename Period>
99 FutureStatus wait_for(std::chrono::duration<Rep, Period> timeout) const;
100
101 /// @brief Waits for value availability until the deadline is reached or until
102 /// the task is cancelled.
103 /// @returns `FutureStatus::kReady` if the value is available.
104 /// @returns `FutureStatus::kTimeout` if `until` time point was reached.
105 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
106 /// @throw std::future_error if Future holds no state.
107 template <typename Clock, typename Duration>
108 FutureStatus wait_until(std::chrono::time_point<Clock, Duration> until) const;
109
110 /// @brief Waits for value availability until the deadline is reached or until
111 /// the task is cancelled.
112 /// @returns `FutureStatus::kReady` if the value is available.
113 /// @returns `FutureStatus::kTimeout` if `deadline` was reached.
114 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
115 /// @throw std::future_error if Future holds no state.
116 FutureStatus wait_until(Deadline deadline) const;
117
118 /// Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAnyContext and friends.
119 AwaitableToken GetAwaitableToken() noexcept USERVER_IMPL_LIFETIME_BOUND {
120 return state_ ? state_->GetAwaitableToken() : AwaitableToken{};
121 }
122
123private:
124 friend class Promise<T>;
125
126 explicit Future(std::shared_ptr<impl::FutureState<T>> state);
127
128 void CheckValid() const;
129
130 std::shared_ptr<impl::FutureState<T>> state_;
131};
132
133template <typename T>
134class Promise final {
135public:
136 /// Creates a new asynchronous value store.
137 Promise();
138
139 ~Promise();
140
141 Promise(const Promise&) = delete;
142 Promise(Promise&&) noexcept = default;
143 Promise& operator=(const Promise&) = delete;
144 Promise& operator=(Promise&&) noexcept;
145
146 /// Retrieves the Future associated with this value store.
147 /// @throw std::future_error if the Future has already been retrieved.
148 [[nodiscard]] Future<T> get_future();
149
150 /// Stores a value for retrieval.
151 /// @throw std::future_error if a value or an exception has already been set.
152 void set_value(const T&);
153
154 /// Stores a value for retrieval.
155 /// @throw std::future_error if a value or an exception has already been set.
156 void set_value(T&&);
157
158 /// Stores an exception to be thrown on retrieval.
159 /// @throw std::future_error if a value or an exception has already been set.
160 void set_exception(std::exception_ptr ex);
161
162private:
163 std::shared_ptr<impl::FutureState<T>> state_;
164};
165
166template <>
167class Promise<void> final {
168public:
169 /// Creates a new asynchronous signal store.
170 Promise();
171
172 ~Promise();
173
174 Promise(const Promise&) = delete;
175 Promise(Promise&&) noexcept = default;
176 Promise& operator=(const Promise&) = delete;
177 Promise& operator=(Promise&&) noexcept;
178
179 /// Retrieves the Future associated with this signal store.
180 /// @throw std::future_error if the Future has already been retrieved.
181 [[nodiscard]] Future<void> get_future();
182
183 /// Stores a signal for retrieval.
184 /// @throw std::future_error if a signal or an exception has already been set.
185 void set_value();
186
187 /// Stores an exception to be thrown on retrieval.
188 /// @throw std::future_error if a signal or an exception has already been set.
189 void set_exception(std::exception_ptr ex);
190
191private:
192 std::shared_ptr<impl::FutureState<void>> state_;
193};
194
195template <typename T>
196bool Future<T>::valid() const noexcept {
197 return !!state_;
198}
199
200template <typename T>
201bool Future<T>::is_ready() const {
202 CheckValid();
203 return state_->IsReady();
204}
205
206template <typename T>
207T Future<T>::get() {
208 CheckValid();
209 return std::exchange(state_, nullptr)->Get();
210}
211
212template <typename T>
213FutureStatus Future<T>::wait() const {
214 CheckValid();
215 return state_->WaitUntil({});
216}
217
218template <typename T>
219template <typename Rep, typename Period>
220FutureStatus Future<T>::wait_for(std::chrono::duration<Rep, Period> timeout) const {
221 return wait_until(Deadline::FromDuration(timeout));
222}
223
224template <typename T>
225template <typename Clock, typename Duration>
226FutureStatus Future<T>::wait_until(std::chrono::time_point<Clock, Duration> until) const {
227 return wait_until(Deadline::FromTimePoint(until));
228}
229
230template <typename T>
231FutureStatus Future<T>::wait_until(Deadline deadline) const {
232 CheckValid();
233 return state_->WaitUntil(deadline);
234}
235
236template <typename T>
237Future<T>::Future(std::shared_ptr<impl::FutureState<T>> state)
238 : state_(std::move(state))
239{
240 CheckValid();
241 state_->OnFutureCreated();
242}
243
244template <typename T>
245void Future<T>::CheckValid() const {
246 if (!state_) {
247 throw std::future_error(std::future_errc::no_state);
248 }
249}
250
251template <typename T>
253 : state_(std::make_shared<impl::FutureState<T>>())
254{}
255
256template <typename T>
257Promise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept {
258 if (this == &other) {
259 return *this;
260 }
261 {
262 [[maybe_unused]] const auto for_destruction = std::move(*this);
263 }
264 state_ = std::move(other.state_);
265 return *this;
266}
267
268template <typename T>
269Promise<T>::~Promise() {
270 if (state_ && !state_->IsReady() && state_->IsFutureCreated()) {
271 try {
272 state_->SetException(std::make_exception_ptr(std::future_error(std::future_errc::broken_promise)));
273 } catch (const std::future_error&) {
274 UASSERT_MSG(false, "Invalid promise usage");
275 }
276 }
277}
278
279template <typename T>
280Future<T> Promise<T>::get_future() {
281 return Future<T>(state_);
282}
283
284template <typename T>
285void Promise<T>::set_value(const T& value) {
286 state_->SetValue(value);
287}
288
289template <typename T>
290void Promise<T>::set_value(T&& value) {
291 state_->SetValue(std::move(value));
292}
293
294template <typename T>
295void Promise<T>::set_exception(std::exception_ptr ex) {
296 state_->SetException(std::move(ex));
297}
298
299inline Promise<void>::Promise()
300 : state_(std::make_shared<impl::FutureState<void>>())
301{}
302
303inline Promise<void>& Promise<void>::operator=(Promise<void>&& other) noexcept {
304 if (this == &other) {
305 return *this;
306 }
307 {
308 [[maybe_unused]] const auto for_destruction = std::move(*this);
309 }
310 state_ = std::move(other.state_);
311 return *this;
312}
313
314inline Promise<void>::~Promise() {
315 if (state_ && !state_->IsReady() && state_->IsFutureCreated()) {
316 try {
317 state_->SetException(std::make_exception_ptr(std::future_error(std::future_errc::broken_promise)));
318 } catch (const std::future_error&) {
319 UASSERT_MSG(false, "Invalid promise usage");
320 }
321 }
322}
323
324inline Future<void> Promise<void>::get_future() { return Future<void>(state_); }
325
326inline void Promise<void>::set_value() { state_->SetValue(); }
327
328inline void Promise<void>::set_exception(std::exception_ptr ex) { state_->SetException(std::move(ex)); }
329
330} // namespace engine
331
332USERVER_NAMESPACE_END