userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
single_consumer_event.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/engine/single_consumer_event.hpp
4
/// @brief @copybrief engine::SingleConsumerEvent
5
6
#
include
<
chrono
>
7
8
#
include
<
userver
/
engine
/
awaitable
.
hpp
>
9
#
include
<
userver
/
engine
/
deadline
.
hpp
>
10
#
include
<
userver
/
engine
/
future_status
.
hpp
>
11
#
include
<
userver
/
utils
/
fast_pimpl
.
hpp
>
12
13
USERVER_NAMESPACE_BEGIN
14
15
namespace
engine {
16
17
/// @ingroup userver_concurrency
18
///
19
/// @brief A multiple-producers, single-consumer event
20
class
SingleConsumerEvent
final
{
21
public
:
22
struct
NoAutoReset
final
{};
23
24
/// Creates an event that resets automatically on retrieval.
25
SingleConsumerEvent
()
noexcept
;
26
27
/// Creates an event that does not reset automatically.
28
explicit
SingleConsumerEvent
(NoAutoReset)
noexcept
;
29
30
SingleConsumerEvent(
const
SingleConsumerEvent&) =
delete
;
31
SingleConsumerEvent(SingleConsumerEvent&&) =
delete
;
32
SingleConsumerEvent& operator=(
const
SingleConsumerEvent&) =
delete
;
33
SingleConsumerEvent& operator=(SingleConsumerEvent&&) =
delete
;
34
~SingleConsumerEvent();
35
36
/// @return whether this event resets automatically on retrieval
37
bool
IsAutoReset
()
const
noexcept
;
38
39
/// @brief Waits until the event is in a signaled state.
40
///
41
/// If the event is auto-resetting, clears the signal flag upon waking up. If
42
/// already in a signaled state, does the same without sleeping.
43
///
44
/// If the waiting failed (the event did not signal), because the optional
45
/// deadline is expired or the current task is cancelled, returns `false`.
46
///
47
/// @return whether the event signaled
48
[[nodiscard]]
bool
WaitForEvent
();
49
50
/// @overload bool WaitForEvent()
51
template
<
typename
Clock,
typename
Duration>
52
[[nodiscard]]
bool
WaitForEventFor(std::chrono::duration<Clock, Duration>);
53
54
/// @overload bool WaitForEvent()
55
template
<
typename
Clock,
typename
Duration>
56
[[nodiscard]]
bool
WaitForEventUntil(std::chrono::time_point<Clock, Duration>);
57
58
/// @brief Waits until the event is signaled, the deadline is reached, or the task is cancelled
59
[[nodiscard]]
bool
WaitForEventUntil
(Deadline);
60
61
/// @brief Waits until the event is in a signaled state, same as
62
/// #WaitForEventUntil, but gives the precise reason of a failure instead of just
63
/// `false`.
64
///
65
/// If the event is auto-resetting, clears the signal flag upon waking up. If already in a signaled state,
66
/// does the same without sleeping.
67
///
68
/// @return `FutureStatus::kReady` if the event signaled, `FutureStatus::kCancelled` if the current task was
69
/// cancelled, `FutureStatus::kTimeout` if the deadline was reached.
70
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline deadline);
71
72
/// @brief Works like `std::condition_variable::wait_until`. Waits until
73
/// @a stop_waiting becomes `true`, and we are notified via `Send`.
74
///
75
/// If @a stop_waiting is already `true`, returns right away.
76
///
77
/// Unlike `std::condition_variable` and engine::ConditionVariable, there are
78
/// no locks around the state watched by @a stop_waiting, so that state must
79
/// be atomic. `std::memory_order_relaxed` is OK inside @a stop_waiting and
80
/// inside the notifiers as long as it does not mess up their logic.
81
///
82
/// **Example.** Suppose we want to wait until a counter is even, then grab
83
/// it.
84
///
85
/// Initialization:
86
/// @snippet core/src/engine/single_consumer_event_test.cpp CV init
87
///
88
/// Notifier side:
89
/// @snippet core/src/engine/single_consumer_event_test.cpp CV notifier
90
///
91
/// Waiter side:
92
/// @snippet core/src/engine/single_consumer_event_test.cpp CV waiter
93
///
94
/// @return `FutureStatus::kReady` if @a stop_waiting became `true`, `FutureStatus::kCancelled` if the current
95
/// task was cancelled, `FutureStatus::kTimeout` if the deadline was reached.
96
template
<
typename
Predicate>
97
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline, Predicate stop_waiting);
98
99
/// Resets the signal flag, if there is any existing event. Guarantees at least 'acquire' and 'release'
100
/// memory ordering. Must only be called by the waiting task.
101
void
Reset
()
noexcept
;
102
103
/// Sets the signal flag and wakes a task that waits on it (if any).
104
/// If the signal flag is already set, does nothing.
105
///
106
/// The waiter is allowed to destroy the SingleConsumerEvent immediately
107
/// after exiting WaitForEvent, ONLY IF the wait succeeded. Otherwise
108
/// a concurrent task may call Send on a destroyed SingleConsumerEvent.
109
/// Here is an example of this situation:
110
/// @snippet core/src/engine/single_consumer_event_test.cpp Wait and destroy
111
///
112
/// You can safely invoke Send from outside a coroutine.
113
void
Send
();
114
115
/// Returns `true` iff already signaled. Never resets the signal.
116
[[nodiscard]]
bool
IsReady
()
const
noexcept
;
117
118
/// @brief Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAnyContext and friends.
119
///
120
/// @note When using `SingleConsumerEvent` as a condition variable, beware of spurious wakeups.
121
/// The awaitable signals completion as soon as @ref Send is called regardless of possible semantic restrictions
122
/// of the predicate in @ref WaitUntil.
123
///
124
/// @warning Only available for @ref NoAutoReset case.
125
AwaitableToken
GetAwaitableToken
();
126
127
private
:
128
struct
Impl;
129
130
bool
GetIsSignaled()
noexcept
;
131
132
utils
::FastPimpl<Impl, 32, 16> impl_;
133
};
134
135
template
<
typename
Clock,
typename
Duration>
136
bool
SingleConsumerEvent::WaitForEventFor(std::chrono::duration<Clock, Duration> duration) {
137
return
WaitForEventUntil(Deadline::FromDuration(duration));
138
}
139
140
template
<
typename
Clock,
typename
Duration>
141
bool
SingleConsumerEvent::WaitForEventUntil(std::chrono::time_point<Clock, Duration> time_point) {
142
return
WaitForEventUntil(Deadline::FromTimePoint(time_point));
143
}
144
145
template
<
typename
Predicate>
146
FutureStatus
SingleConsumerEvent::
WaitUntil
(Deadline deadline, Predicate stop_waiting) {
147
// If the state, according to what we've been previously notified of via
148
// 'Send', is OK, then return right away. Fresh state updates can also
149
// leak to us here, but we should not rely on it.
150
while
(!stop_waiting()) {
151
// Wait until we are allowed to make progress.
152
// On the first such wait, we may discover a signal from the state that
153
// has already leaked to us previously (as described above).
154
//
155
// We may also receive false signals from cases when we are allowed
156
// and unallowed to make progress in a rapid sequence, or when the notifier
157
// thinks that we might be happy with the state, but we aren't.
158
if
(
const
auto
status =
WaitUntil
(
deadline
)
; status !=
FutureStatus
::
kReady
) {
159
return
status;
160
}
161
162
if
(!
IsAutoReset
(
)
) {
163
// Reset guarantees `std::memory_order_acquire` on the signal, so
164
// if we reset any additional signals here, then the predicate will
165
// see the associated data updates.
166
Reset
(
)
;
167
}
168
}
169
170
return
FutureStatus
::
kReady
;
171
}
172
173
}
// namespace engine
174
175
USERVER_NAMESPACE_END
userver
engine
single_consumer_event.hpp
Generated on
for userver by
Doxygen
1.17.0