userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
pulse_event.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/engine/pulse_event.hpp
4
/// @brief @copybrief engine::PulseEvent
5
6
#
include
<
atomic
>
7
#
include
<
cstdint
>
8
9
#
include
<
userver
/
compiler
/
impl
/
lifetime
.
hpp
>
10
#
include
<
userver
/
engine
/
awaitable
.
hpp
>
11
#
include
<
userver
/
engine
/
deadline
.
hpp
>
12
#
include
<
userver
/
engine
/
future_status
.
hpp
>
13
#
include
<
userver
/
engine
/
impl
/
context_accessor
.
hpp
>
14
#
include
<
userver
/
engine
/
impl
/
wait_list_fwd
.
hpp
>
15
#
include
<
userver
/
utils
/
fast_pimpl
.
hpp
>
16
17
USERVER_NAMESPACE_BEGIN
18
19
namespace
engine {
20
21
class
PulseEvent;
22
23
/// @brief A one-shot subscription from @ref PulseEvent::Subscribe.
24
///
25
/// Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAnyContext and friends.
26
///
27
/// @note For simple "subscribe to updates" cases a callback API is often more convenient:
28
/// @ref concurrent::AsyncEventChannel.
29
///
30
/// **Example.** A parent task publishes two atomic config fields and wakes all consumers. Each consumer
31
/// assembles a `Config` and passes it to `ApplyConfig`.
32
///
33
/// Initialization:
34
/// @snippet core/src/engine/pulse_event_test.cpp WaitUntil subscription init
35
///
36
/// Publisher side:
37
/// @snippet core/src/engine/pulse_event_test.cpp WaitUntil subscription notifier
38
///
39
/// Consumer side:
40
/// @snippet core/src/engine/pulse_event_test.cpp WaitUntil subscription waiter
41
class
PulseEventSubscription
final
:
private
impl
::AwaitableBase {
42
public
:
43
PulseEventSubscription()
noexcept
;
44
PulseEventSubscription(
const
PulseEventSubscription&) =
delete
;
45
PulseEventSubscription(PulseEventSubscription&& other)
noexcept
;
46
PulseEventSubscription& operator=(
const
PulseEventSubscription&) =
delete
;
47
PulseEventSubscription& operator=(PulseEventSubscription&& other)
noexcept
;
48
~PulseEventSubscription();
49
50
/// @return `true` if this object was issued by @ref PulseEvent::Subscribe
51
[[nodiscard]]
bool
IsValid
()
const
noexcept
;
52
53
/// @return `true` if a @ref PulseEvent::Send after this subscription has already happened
54
[[nodiscard]]
bool
IsReady
()
const
noexcept
override
;
55
56
/// @brief Waits until a concurrent @ref PulseEvent::Send satisfies this subscription, or the deadline expires, or
57
/// the current task is cancelled.
58
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline);
59
60
/// Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAnyContext and friends.
61
AwaitableToken
GetAwaitableToken
()
noexcept
USERVER_IMPL_LIFETIME_BOUND;
62
63
private
:
64
friend
class
PulseEvent;
65
66
using
Ticket = std::uint64_t;
67
68
PulseEventSubscription(PulseEvent* event, Ticket ticket)
noexcept
;
69
70
void
TryAppendAwaiter(
impl
::AwaiterPtr& awaiter, std::uintptr_t context)
override
;
71
impl
::AwaiterPtr RemoveAwaiter(
impl
::Awaiter& awaiter, std::uintptr_t context)
noexcept
override
;
72
73
PulseEvent* event_{
nullptr
};
74
Ticket ticket_{};
75
};
76
77
/// @ingroup userver_concurrency
78
///
79
/// @brief A multiple-producers, multiple-consumers notification.
80
///
81
/// @ref Subscribe issues a one-shot @ref PulseEventSubscription. Any @ref Send after that satisfies it; a @ref Send
82
/// that happened before @ref Subscribe does not count. After that first @ref Send, the subscription stays ready;
83
/// call @ref Subscribe again to wait for later signals.
84
///
85
/// This is unlike @ref engine::MultiConsumerEvent, which latches a single global signal forever after the first
86
/// @ref engine::MultiConsumerEvent::Send "Send" (equivalent to @ref engine::SingleConsumerEvent::NoAutoReset).
87
///
88
/// @ref PulseEventSubscription is compatible with @ref engine::WaitAny and friends. `PulseEvent` itself is not
89
/// an awaitable: wait on a subscription from @ref Subscribe.
90
///
91
/// @see @ref scripts/docs/en/userver/synchronization.md "Synchronization primitives (PulseEvent overview and example)"
92
class
PulseEvent
final
{
93
using
Ticket = std::uint64_t;
94
95
public
:
96
PulseEvent()
noexcept
;
97
98
PulseEvent(
const
PulseEvent&) =
delete
;
99
PulseEvent(PulseEvent&&) =
delete
;
100
PulseEvent& operator=(
const
PulseEvent&) =
delete
;
101
PulseEvent& operator=(PulseEvent&&) =
delete
;
102
~PulseEvent();
103
104
/// @brief Issues a one-shot subscription that later @ref Send calls can satisfy.
105
[[nodiscard]] PulseEventSubscription
Subscribe
()
noexcept
;
106
107
/// @brief Works like `std::condition_variable::wait_until`. Waits until @a stop_waiting becomes `true`, and we are
108
/// notified via `Send`.
109
///
110
/// If @a stop_waiting is already `true`, returns right away.
111
///
112
/// Unlike `std::condition_variable` and engine::ConditionVariable, there are no locks around the state watched by
113
/// @a stop_waiting, so that state must be atomic. `std::memory_order_relaxed` is OK inside @a stop_waiting and
114
/// inside the notifiers as long as it does not mess up their logic.
115
///
116
/// For a full example, see @ref engine_pulse_event.
117
///
118
/// @return `FutureStatus::kReady` if @a stop_waiting became `true`, `FutureStatus::kCancelled` if the current task
119
/// was cancelled, `FutureStatus::kTimeout` if the deadline was reached.
120
template
<
typename
Predicate>
121
[[nodiscard]]
FutureStatus
WaitUntil
(Deadline, Predicate stop_waiting);
122
123
/// Wakes all tasks that currently wait on this event, if any. Also satisfies every subscription already issued, so
124
/// a @ref Send that races with @ref Subscribe is not lost.
125
///
126
/// You can safely invoke Send from outside a coroutine.
127
void
Send
()
noexcept
;
128
129
private
:
130
friend
class
PulseEventSubscription;
131
132
std::atomic<Ticket> generation_{0};
133
impl
::FastPimplWaitList awaiters_;
134
};
135
136
template
<
typename
Predicate>
137
FutureStatus
PulseEvent::
WaitUntil
(Deadline deadline, Predicate stop_waiting) {
138
while
(
true
) {
139
auto
subscription =
Subscribe
(
)
;
140
141
if
(stop_waiting()) {
142
return
FutureStatus
::
kReady
;
143
}
144
145
if
(
const
auto
status = subscription
.
WaitUntil
(
deadline
)
; status !=
FutureStatus
::
kReady
) {
146
return
status;
147
}
148
}
149
}
150
151
}
// namespace engine
152
153
USERVER_NAMESPACE_END
userver
engine
pulse_event.hpp
Generated on
for userver by
Doxygen
1.17.0