userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
periodic_task.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/utils/periodic_task.hpp
4
/// @brief @copybrief utils::PeriodicTask
5
6
#
include
<
chrono
>
7
#
include
<
functional
>
8
#
include
<
optional
>
9
#
include
<
string
>
10
11
#
include
<
userver
/
logging
/
level
.
hpp
>
12
#
include
<
userver
/
utils
/
assert
.
hpp
>
13
#
include
<
userver
/
utils
/
fast_pimpl
.
hpp
>
14
#
include
<
userver
/
utils
/
flags
.
hpp
>
15
16
USERVER_NAMESPACE_BEGIN
17
18
namespace
engine {
19
class
TaskProcessor;
20
}
// namespace engine
21
22
namespace
testsuite {
23
class
PeriodicTaskControl;
24
class
TestsuiteTasks;
25
}
// namespace testsuite
26
27
namespace
utils
{
28
29
/// @ingroup userver_concurrency
30
///
31
/// @brief Task that periodically runs a user callback. Callback is started
32
/// after the previous callback execution is finished every `period + A - B`,
33
/// where:
34
/// * `A` is `+/- distribution * rand(0.0, 1.0)` if Flags::kChaotic flag is set,
35
/// otherwise is `0`;
36
/// * `B` is the time of previous callback execution if Flags::kStrong flag is
37
/// set, otherwise is `0`;
38
///
39
/// TaskProcessor to execute the callback and many other options are specified
40
/// in PeriodicTask::Settings.
41
class
PeriodicTask
final
{
42
public
:
43
enum
class
Flags
{
44
/// None of the below flags
45
kNone
= 0,
46
/// Immediately call a function
47
kNow
= 1 << 0,
48
/// Account function call time as a part of wait period
49
kStrong
= 1 << 1,
50
/// Randomize wait period (+-25% by default)
51
kChaotic
= 1 << 2,
52
/// @deprecated Does nothing, PeriodicTask is always spawned as
53
/// `engine::Task::Importance::kCritical`.
54
/// @note Although this periodic task cannot be cancelled due to
55
/// system overload, it's cancelled upon calling `Stop`.
56
/// Subtasks that may be spawned in the callback
57
/// are not critical by default and may be cancelled as usual.
58
kCritical
= 1 << 4,
59
};
60
61
/// Configuration parameters for PeriodicTask.
62
struct
Settings
final
{
63
static
constexpr
uint8_t kDistributionPercent = 25;
64
65
constexpr
/*implicit*/
Settings(
66
std::chrono::milliseconds period,
67
utils
::Flags<
Flags
> flags = {},
68
logging::
Level
span_level = logging
::
Level
::
kInfo
69
)
70
: Settings(period, kDistributionPercent, flags, span_level)
71
{}
72
73
constexpr
Settings(
74
std::chrono::milliseconds period,
75
std::chrono::milliseconds distribution,
76
utils
::Flags<
Flags
> flags = {},
77
logging::
Level
span_level = logging
::
Level
::
kInfo
78
)
79
:
period
(period),
80
distribution
(distribution),
81
flags
(flags),
82
span_level
(span_level)
83
{
84
UASSERT
(distribution <= period);
85
}
86
87
constexpr
Settings(
88
std::chrono::milliseconds period,
89
uint8_t distribution_percent,
90
utils
::Flags<
Flags
> flags = {},
91
logging::
Level
span_level = logging
::
Level
::
kInfo
92
)
93
: Settings(period, period * distribution_percent / 100, flags, span_level)
94
{
95
UASSERT
(distribution_percent <= 100);
96
}
97
98
template
<
class
Rep,
class
Period>
99
constexpr
/*implicit*/
Settings(std::chrono::duration<Rep, Period> period)
100
: Settings(period, kDistributionPercent, {}, logging
::
Level
::
kInfo
)
101
{}
102
103
bool
operator==(
const
Settings& other)
const
noexcept
;
104
105
// Note: Tidy requires us to explicitly initialize these fields, although
106
// the initializers are never used.
107
108
/// @brief Period for the task execution. Task is repeated every
109
/// `(period +/- distribution) - time of previous execution`
110
std::chrono::milliseconds
period
{};
111
112
/// @brief Jitter for task repetitions. If kChaotic is set in `flags`
113
/// the task is repeated every
114
/// `(period +/- distribution) - time of previous execution`
115
std::chrono::milliseconds
distribution
{};
116
117
/// @brief Used instead of `period` in case of exception, if set.
118
std::optional<std::chrono::milliseconds>
exception_period
;
119
120
/// @brief Flags that control the behavior of PeriodicTask.
121
utils
::Flags<
Flags
>
flags
{};
122
123
/// @brief tracing::Span that measures each execution of the task
124
/// uses this logging level.
125
logging::
Level
span_level
{logging
::
Level
::
kInfo
};
126
127
/// @brief TaskProcessor to execute the task. If nullptr then the
128
/// PeriodicTask::Start() calls engine::current_task::GetTaskProcessor()
129
/// to get the TaskProcessor.
130
engine::TaskProcessor*
task_processor
{
nullptr
};
131
};
132
133
/// Signature of the task to be executed each period.
134
using
Callback
= std::function<
void
()>;
135
136
/// Default constructor that does nothing.
137
PeriodicTask
();
138
139
PeriodicTask(PeriodicTask&&) =
delete
;
140
PeriodicTask(
const
PeriodicTask&) =
delete
;
141
142
/// Constructs the periodic task and calls Start()
143
/// @see StartPeriodicTask
144
PeriodicTask
(std::string name, Settings settings,
Callback
callback);
145
146
/// Stops the periodic execution of previous task and starts the periodic
147
/// execution of the new task.
148
/// @see StartPeriodicTask
149
void
Start
(std::string name, Settings settings,
Callback
callback);
150
151
~PeriodicTask();
152
153
/// @brief Stops the PeriodicTask. If a Step() is in progress, cancels it and
154
/// waits for its completion.
155
/// @warning PeriodicTask must be stopped before the callback becomes invalid.
156
/// E.g. if your class X stores PeriodicTask and the callback is class' X
157
/// method, you have to explicitly stop PeriodicTask in ~X() as after ~X()
158
/// exits the object is destroyed and using X's 'this' in callback is UB.
159
void
Stop
()
noexcept
;
160
161
/// Set all settings except flags. All flags must be set at the start.
162
void
SetSettings
(Settings settings);
163
164
/// @brief Non-blocking force next iteration.
165
///
166
/// Returns immediately, without waiting for Step() to finish.
167
///
168
/// - If PeriodicTask isn't running, then a Step() will be performed at the
169
/// start.
170
/// - If the PeriodicTask is waiting for the next iteration, then the wait is
171
/// interrupted and the next Step() is executed.
172
/// - If Step() is being executed, the current iteration will be completed and
173
/// only after that a new iteration will be called. Reason: the current
174
/// iteration is considered to be using stale data.
175
///
176
/// @note If 'ForceStepAsync' is called multiple times while Step() is
177
/// being executed, all events will be conflated (one extra Step() call will
178
/// be executed).
179
void
ForceStepAsync
();
180
181
/// Force next DoStep() iteration. It is guaranteed that there is at least one
182
/// call to DoStep() during SynchronizeDebug() execution. DoStep() is executed
183
/// as usual in the PeriodicTask's task (NOT in current task).
184
/// @param preserve_span run periodic task current span if true. It's here for
185
/// backward compatibility with existing tests. Will be removed in
186
/// TAXIDATA-1499.
187
/// @returns true if task was successfully executed.
188
/// @note On concurrent invocations, the task is guaranteed to be invoked
189
/// serially, one time after another.
190
bool
SynchronizeDebug
(
bool
preserve_span =
false
);
191
192
/// Skip Step() calls from loop until ResumeDebug() is called. If DoStep()
193
/// is executing, wait its completion, for a potentially long time.
194
/// The purpose is to control task execution from tests.
195
void
SuspendDebug
();
196
197
/// Stop skipping Step() calls from loop. Returns without waiting for
198
/// DoStep() call. The purpose is to control task execution from tests.
199
void
ResumeDebug
();
200
201
/// Checks if a periodic task (not a single iteration only) is running.
202
/// It may be in a callback execution or sleeping between callbacks.
203
bool
IsRunning
()
const
;
204
205
/// Make this periodic task available for testsuite. Testsuite provides a way
206
/// to call it directly from testcase.
207
void
RegisterInTestsuite
(USERVER_NAMESPACE::testsuite::PeriodicTaskControl& periodic_task_control);
208
209
/// Get current settings. Note that they might become stale very quickly.
210
Settings
GetCurrentSettings
()
const
;
211
212
private
:
213
class
Impl;
214
constexpr
static
std::size_t kSize = 448;
215
constexpr
static
std::size_t kAlignment = 16;
216
utils
::FastPimpl<Impl, kSize, kAlignment> impl_;
217
};
218
219
/// Starts PeriodicTask for high-level business tasks.
220
/// It checks for testsuite and either:
221
/// - registers the task in testsuite wihtout actual periodic start
222
/// (in testsuite environment);
223
/// - actually starts the periodic (otherwise).
224
///
225
/// This function makes non-deterministic PeriodicTask testable and predictable.
226
/// PeriodicTask::Start might lead to unexpected behaviour in tests (especially between the tests) and, as a result, to
227
/// flaky tests. Use raw PeriodicTask::Start only for low-level maintenance jobs like network keepalive or other
228
/// housekeeping.
229
void
StartPeriodicTask
(
230
PeriodicTask& periodic_task,
231
std::string name,
232
const
PeriodicTask::Settings& settings,
233
PeriodicTask::
Callback
callback,
234
testsuite::TestsuiteTasks& testsuite_tasks
235
);
236
237
}
// namespace utils
238
239
USERVER_NAMESPACE_END
userver
utils
periodic_task.hpp
Generated on
for userver by
Doxygen
1.17.0