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