Github   Telegram
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 md_en_userver_synchronization
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 md_en_userver_synchronization
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 = default;
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 = default;
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() {
239 if (state_ && !state_->IsReady()) {
240 try {
241 state_->SetException(std::make_exception_ptr(
242 std::future_error(std::future_errc::broken_promise)));
243 } catch (const std::future_error&) {
244 UASSERT_MSG(false, "Invalid promise usage");
245 }
246 }
247}
248
249template <typename T>
250Future<T> Promise<T>::get_future() {
251 return Future<T>(state_);
252}
253
254template <typename T>
255void Promise<T>::set_value(const T& value) {
256 state_->SetValue(value);
257}
258
259template <typename T>
260void Promise<T>::set_value(T&& value) {
261 state_->SetValue(std::move(value));
262}
263
264template <typename T>
265void Promise<T>::set_exception(std::exception_ptr ex) {
266 state_->SetException(std::move(ex));
267}
268
269inline Promise<void>::Promise()
270 : state_(std::make_shared<impl::FutureState<void>>()) {}
271
272inline Promise<void>::~Promise() {
273 if (state_ && !state_->IsReady()) {
274 try {
275 state_->SetException(std::make_exception_ptr(
276 std::future_error(std::future_errc::broken_promise)));
277 } catch (const std::future_error&) {
278 UASSERT_MSG(false, "Invalid promise usage");
279 }
280 }
281}
282
283inline Future<void> Promise<void>::get_future() { return Future<void>(state_); }
284
285inline void Promise<void>::set_value() { state_->SetValue(); }
286
287inline void Promise<void>::set_exception(std::exception_ptr ex) {
288 state_->SetException(std::move(ex));
289}
290
291} // namespace engine
292
293USERVER_NAMESPACE_END