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 {
52public:
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 /// @brief Returns whether this Future holds a valid state.
62 bool valid() const noexcept;
63
64 /// @brief Returns whether the value is available.
65 /// @warning If value is set and task is being canceled or if Promise has been
66 /// destroyed without setting a value, returns true.
67 /// @throw std::future_error if Future holds no state or if the value has
68 /// already been retrieved.
69 bool is_ready() const;
70
71 /// @brief Waits for value availability and retrieves it.
72 /// @throw WaitInterruptedException if the current task has been cancelled in
73 /// the process.
74 /// @throw std::future_error if Future holds no state, if the Promise has
75 /// been destroyed without setting a value or if the value has already been
76 /// retrieved.
77 T get();
78
79 /// @brief Waits for value availability.
80 /// @returns `FutureStatus::kReady` if the value is available.
81 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
82 /// @throw std::future_error if Future holds no state.
83 [[nodiscard]] FutureStatus wait() const;
84
85 /// @brief Waits for value availability until the timeout expires or until the
86 /// task is cancelled.
87 /// @returns `FutureStatus::kReady` if the value is available.
88 /// @returns `FutureStatus::kTimeout` if `timeout` has expired.
89 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
90 /// @throw std::future_error if Future holds no state.
91 template <typename Rep, typename Period>
92 FutureStatus wait_for(std::chrono::duration<Rep, Period> timeout) const;
93
94 /// @brief Waits for value availability until the deadline is reached or until
95 /// the task is cancelled.
96 /// @returns `FutureStatus::kReady` if the value is available.
97 /// @returns `FutureStatus::kTimeout` if `until` time point was reached.
98 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
99 /// @throw std::future_error if Future holds no state.
100 template <typename Clock, typename Duration>
101 FutureStatus wait_until(std::chrono::time_point<Clock, Duration> until) const;
102
103 /// @brief Waits for value availability until the deadline is reached or until
104 /// the task is cancelled.
105 /// @returns `FutureStatus::kReady` if the value is available.
106 /// @returns `FutureStatus::kTimeout` if `deadline` was reached.
107 /// @returns `FutureStatus::kCancelled` if current task is being cancelled.
108 /// @throw std::future_error if Future holds no state.
109 FutureStatus wait_until(Deadline deadline) const;
110
111 /// @cond
112 // Internal helper for WaitAny/WaitAll
113 impl::ContextAccessor* TryGetContextAccessor() noexcept {
114 return state_ ? state_->TryGetContextAccessor() : nullptr;
115 }
116 /// @endcond
117
118private:
119 friend class Promise<T>;
120
121 explicit Future(std::shared_ptr<impl::FutureState<T>> state);
122
123 void CheckValid() const;
124
125 std::shared_ptr<impl::FutureState<T>> state_;
126};
127
128template <typename T>
129class Promise final {
130public:
131 /// Creates a new asynchronous value store.
132 Promise();
133
134 ~Promise();
135
136 Promise(const Promise&) = delete;
137 Promise(Promise&&) noexcept = default;
138 Promise& operator=(const Promise&) = delete;
139 Promise& operator=(Promise&&) noexcept;
140
141 /// Retrieves the Future associated with this value store.
142 /// @throw std::future_error if the Future has already been retrieved.
143 [[nodiscard]] Future<T> get_future();
144
145 /// Stores a value for retrieval.
146 /// @throw std::future_error if a value or an exception has already been set.
147 void set_value(const T&);
148
149 /// Stores a value for retrieval.
150 /// @throw std::future_error if a value or an exception has already been set.
151 void set_value(T&&);
152
153 /// Stores an exception to be thrown on retrieval.
154 /// @throw std::future_error if a value or an exception has already been set.
155 void set_exception(std::exception_ptr ex);
156
157private:
158 std::shared_ptr<impl::FutureState<T>> state_;
159};
160
161template <>
162class Promise<void> final {
163public:
164 /// Creates a new asynchronous signal store.
165 Promise();
166
167 ~Promise();
168
169 Promise(const Promise&) = delete;
170 Promise(Promise&&) noexcept = default;
171 Promise& operator=(const Promise&) = delete;
172 Promise& operator=(Promise&&) noexcept;
173
174 /// Retrieves the Future associated with this signal store.
175 /// @throw std::future_error if the Future has already been retrieved.
176 [[nodiscard]] Future<void> get_future();
177
178 /// Stores a signal for retrieval.
179 /// @throw std::future_error if a signal or an exception has already been set.
180 void set_value();
181
182 /// Stores an exception to be thrown on retrieval.
183 /// @throw std::future_error if a signal or an exception has already been set.
184 void set_exception(std::exception_ptr ex);
185
186private:
187 std::shared_ptr<impl::FutureState<void>> state_;
188};
189
190template <typename T>
191bool Future<T>::valid() const noexcept {
192 return !!state_;
193}
194
195template <typename T>
196bool Future<T>::is_ready() const {
197 CheckValid();
198 return state_->IsReady();
199}
200
201template <typename T>
202T Future<T>::get() {
203 CheckValid();
204 return std::exchange(state_, nullptr)->Get();
205}
206
207template <typename T>
208FutureStatus Future<T>::wait() const {
209 CheckValid();
210 return state_->WaitUntil({});
211}
212
213template <typename T>
214template <typename Rep, typename Period>
215FutureStatus Future<T>::wait_for(std::chrono::duration<Rep, Period> timeout) const {
216 return wait_until(Deadline::FromDuration(timeout));
217}
218
219template <typename T>
220template <typename Clock, typename Duration>
221FutureStatus Future<T>::wait_until(std::chrono::time_point<Clock, Duration> until) const {
222 return wait_until(Deadline::FromTimePoint(until));
223}
224
225template <typename T>
226FutureStatus Future<T>::wait_until(Deadline deadline) const {
227 CheckValid();
228 return state_->WaitUntil(deadline);
229}
230
231template <typename T>
232Future<T>::Future(std::shared_ptr<impl::FutureState<T>> state) : state_(std::move(state)) {
233 CheckValid();
234 state_->OnFutureCreated();
235}
236
237template <typename T>
238void Future<T>::CheckValid() const {
239 if (!state_) {
240 throw std::future_error(std::future_errc::no_state);
241 }
242}
243
244template <typename T>
245Promise<T>::Promise() : state_(std::make_shared<impl::FutureState<T>>()) {}
246
247template <typename T>
248Promise<T>& Promise<T>::operator=(Promise<T>&& other) noexcept {
249 if (this == &other) return *this;
250 { [[maybe_unused]] const auto for_destruction = std::move(*this); }
251 state_ = std::move(other.state_);
252 return *this;
253}
254
255template <typename T>
256Promise<T>::~Promise() {
257 if (state_ && !state_->IsReady() && state_->IsFutureCreated()) {
258 try {
259 state_->SetException(std::make_exception_ptr(std::future_error(std::future_errc::broken_promise)));
260 } catch (const std::future_error&) {
261 UASSERT_MSG(false, "Invalid promise usage");
262 }
263 }
264}
265
266template <typename T>
267Future<T> Promise<T>::get_future() {
268 return Future<T>(state_);
269}
270
271template <typename T>
272void Promise<T>::set_value(const T& value) {
273 state_->SetValue(value);
274}
275
276template <typename T>
277void Promise<T>::set_value(T&& value) {
278 state_->SetValue(std::move(value));
279}
280
281template <typename T>
282void Promise<T>::set_exception(std::exception_ptr ex) {
283 state_->SetException(std::move(ex));
284}
285
286inline Promise<void>::Promise() : state_(std::make_shared<impl::FutureState<void>>()) {}
287
288inline Promise<void>& Promise<void>::operator=(Promise<void>&& other) noexcept {
289 if (this == &other) return *this;
290 { [[maybe_unused]] const auto for_destruction = std::move(*this); }
291 state_ = std::move(other.state_);
292 return *this;
293}
294
295inline Promise<void>::~Promise() {
296 if (state_ && !state_->IsReady() && state_->IsFutureCreated()) {
297 try {
298 state_->SetException(std::make_exception_ptr(std::future_error(std::future_errc::broken_promise)));
299 } catch (const std::future_error&) {
300 UASSERT_MSG(false, "Invalid promise usage");
301 }
302 }
303}
304
305inline Future<void> Promise<void>::get_future() { return Future<void>(state_); }
306
307inline void Promise<void>::set_value() { state_->SetValue(); }
308
309inline void Promise<void>::set_exception(std::exception_ptr ex) { state_->SetException(std::move(ex)); }
310
311} // namespace engine
312
313USERVER_NAMESPACE_END