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