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
/// @overload bool WaitForEvent()
59
[[nodiscard]]
bool
WaitForEventUntil(Deadline);
60
61
/// @brief Waits until the event is in a signaled state, same as @ref WaitForEventUntil, but gives the precise
62
/// reason of a failure instead of just `false`.
63
///
64
/// If the event is auto-resetting, clears the signal flag upon waking up. If already in a signaled state,
65
/// does the same without sleeping.
66
///
67
/// @return `FutureStatus::kReady` if the event signaled, `FutureStatus::kCancelled` if the current task was
68
/// cancelled, `FutureStatus::kTimeout` if the deadline was reached.
69
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline deadline);
70
71
/// @brief Works like `std::condition_variable::wait_until`. Waits until
72
/// @a stop_waiting becomes `true`, and we are notified via `Send`.
73
///
74
/// If @a stop_waiting is already `true`, returns right away.
75
///
76
/// Unlike `std::condition_variable` and engine::ConditionVariable, there are
77
/// no locks around the state watched by @a stop_waiting, so that state must
78
/// be atomic. `std::memory_order_relaxed` is OK inside @a stop_waiting and
79
/// inside the notifiers as long as it does not mess up their logic.
80
///
81
/// **Example.** Suppose we want to wait until a counter is even, then grab
82
/// it.
83
///
84
/// Initialization:
85
/// @snippet engine/single_consumer_event_test.cpp CV init
86
///
87
/// Notifier side:
88
/// @snippet engine/single_consumer_event_test.cpp CV notifier
89
///
90
/// Waiter side:
91
/// @snippet engine/single_consumer_event_test.cpp CV waiter
92
///
93
/// @return `FutureStatus::kReady` if @a stop_waiting became `true`, `FutureStatus::kCancelled` if the current
94
/// task was cancelled, `FutureStatus::kTimeout` if the deadline was reached.
95
template
<
typename
Predicate>
96
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline, Predicate stop_waiting);
97
98
/// Resets the signal flag, if there is any existing event. Guarantees at least 'acquire' and 'release'
99
/// memory ordering. Must only be called by the waiting task.
100
void
Reset
()
noexcept
;
101
102
/// Sets the signal flag and wakes a task that waits on it (if any).
103
/// If the signal flag is already set, does nothing.
104
///
105
/// The waiter is allowed to destroy the SingleConsumerEvent immediately
106
/// after exiting WaitForEvent, ONLY IF the wait succeeded. Otherwise
107
/// a concurrent task may call Send on a destroyed SingleConsumerEvent.
108
/// Here is an example of this situation:
109
/// @snippet engine/single_consumer_event_test.cpp Wait and destroy
110
///
111
/// You can safely invoke Send from outside a coroutine.
112
void
Send
();
113
114
/// Returns `true` iff already signaled. Never resets the signal.
115
[[nodiscard]]
bool
IsReady
()
const
noexcept
;
116
117
/// @brief Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAnyContext and friends.
118
///
119
/// @note When using `SingleConsumerEvent` as a condition variable, beware of spurious wakeups.
120
/// The awaitable signals completion as soon as @ref Send is called regardless of possible semantic restrictions
121
/// of the predicate in @ref WaitUntil.
122
///
123
/// @warning Only available for @ref NoAutoReset case.
124
AwaitableToken
GetAwaitableToken
();
125
126
private
:
127
struct
Impl;
128
129
bool
GetIsSignaled()
noexcept
;
130
131
utils
::FastPimpl<Impl, 32, 16> impl_;
132
};
133
134
template
<
typename
Clock,
typename
Duration>
135
bool
SingleConsumerEvent::WaitForEventFor(std::chrono::duration<Clock, Duration> duration) {
136
return
WaitForEventUntil(Deadline::FromDuration(duration));
137
}
138
139
template
<
typename
Clock,
typename
Duration>
140
bool
SingleConsumerEvent::WaitForEventUntil(std::chrono::time_point<Clock, Duration> time_point) {
141
return
WaitForEventUntil(Deadline::FromTimePoint(time_point));
142
}
143
144
template
<
typename
Predicate>
145
FutureStatus
SingleConsumerEvent::
WaitUntil
(Deadline deadline, Predicate stop_waiting) {
146
// If the state, according to what we've been previously notified of via
147
// 'Send', is OK, then return right away. Fresh state updates can also
148
// leak to us here, but we should not rely on it.
149
while
(!stop_waiting()) {
150
// Wait until we are allowed to make progress.
151
// On the first such wait, we may discover a signal from the state that
152
// has already leaked to us previously (as described above).
153
//
154
// We may also receive false signals from cases when we are allowed
155
// and unallowed to make progress in a rapid sequence, or when the notifier
156
// thinks that we might be happy with the state, but we aren't.
157
if
(
const
auto
status =
WaitUntil
(
deadline
)
; status !=
FutureStatus
::
kReady
) {
158
return
status;
159
}
160
161
if
(!
IsAutoReset
(
)
) {
162
// Reset guarantees `std::memory_order_acquire` on the signal, so
163
// if we reset any additional signals here, then the predicate will
164
// see the associated data updates.
165
Reset
(
)
;
166
}
167
}
168
169
return
FutureStatus
::
kReady
;
170
}
171
172
}
// namespace engine
173
174
USERVER_NAMESPACE_END
userver
engine
single_consumer_event.hpp
Generated on
for userver by
Doxygen
1.17.0