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/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