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