morph
0.1.0
Asynchronous bridge between GUI thread and business models
Toggle main menu visibility
Loading...
Searching...
No Matches
scheduler.hpp
Go to the documentation of this file.
1
// SPDX-License-Identifier: Apache-2.0
2
3
#pragma once
4
#include <chrono>
5
#include <functional>
6
#include <utility>
7
8
/// @file
9
/// @brief `morph::reactive::Scheduler`: timers a frontend runs on the runtime's owner executor.
10
///
11
/// Specified in `docs/spec/reactive/control.md`, "Timed refresh".
12
13
namespace
morph::reactive {
14
15
/// @brief Owns one scheduled timer and cancels it when destroyed. Move-only.
16
///
17
/// A scheduler hands it two functions: one that cancels the timer and one that says whether the timer is still
18
/// scheduled. So `active()` turns false once a one-shot timer has fired, and cancelling a fired timer does nothing.
19
class
TimerHandle
{
20
public
:
21
/// @brief A handle that owns no timer.
22
TimerHandle
() =
default
;
23
24
/// @brief Takes ownership of a timer through the function that cancels it.
25
/// @param cancel Cancels the timer; called at most once. It must tolerate a timer that has already
26
/// fired or whose scheduler is gone.
27
/// @param isScheduled Whether the timer is still scheduled: false once a one-shot timer has fired or its
28
/// scheduler is gone. Empty means the handle cannot tell, and `active()` stays true until `cancel()`.
29
explicit
TimerHandle
(std::function<
void
()>
cancel
, std::function<
bool
()> isScheduled = {})
30
: _cancel{std::move(
cancel
)}, _isScheduled{std::move(isScheduled)} {}
31
32
/// @brief Cancels the timer, if this handle still owns one.
33
~TimerHandle
() {
cancel
(); }
34
35
TimerHandle
(
TimerHandle
const
&) =
delete
;
36
TimerHandle
& operator=(
TimerHandle
const
&) =
delete
;
37
38
/// @brief Takes over @p other's timer; @p other then owns none.
39
/// @param other The handle to move from.
40
TimerHandle
(
TimerHandle
&& other) noexcept
41
: _cancel{std::exchange(other._cancel, {})}, _isScheduled{std::exchange(other._isScheduled, {})} {}
42
43
/// @brief Cancels this handle's timer, then takes over @p other's.
44
/// @param other The handle to move from.
45
/// @return `*this`.
46
TimerHandle
&
operator=
(
TimerHandle
&& other)
noexcept
{
47
if
(
this
!= &other) {
48
cancel
();
49
_cancel = std::exchange(other._cancel, {});
50
_isScheduled = std::exchange(other._isScheduled, {});
51
}
52
return
*
this
;
53
}
54
55
/// @brief Cancels the timer, if this handle still owns one.
56
void
cancel
() noexcept {
57
_isScheduled = {};
58
if
(
auto
const
stop = std::exchange(_cancel, {})) {
59
// Runs in the destructor and in move assignment, which cannot report a failure: a cancel function
60
// that breaks its no-throw contract leaves its timer to the scheduler.
61
try
{
62
stop();
63
}
catch
(...) {
// NOLINT(bugprone-empty-catch): nothing to report to, as said above
64
}
65
}
66
}
67
68
/// @brief Whether this handle still owns a scheduled timer.
69
/// @return False after `cancel()`, after a move from it, for a default-constructed handle, and once a one-shot
70
/// timer has fired or its scheduler is gone (when the scheduler can say so).
71
[[nodiscard]]
bool
active
() const noexcept {
72
if
(!_cancel) {
73
return
false
;
74
}
75
if
(!_isScheduled) {
76
return
true
;
77
}
78
// A probe that breaks its no-throw contract cannot say the timer is still there.
79
try
{
80
return
_isScheduled();
81
}
catch
(...) {
82
return
false
;
83
}
84
}
85
86
private
:
87
std::function<void()> _cancel;
88
std::function<bool()> _isScheduled;
89
};
90
91
/// @brief Runs callbacks after a delay or on a period, on the runtime's owner executor.
92
///
93
/// Every frontend provides one, on its own event loop's timers; a test uses `testing::ManualScheduler`. Every
94
/// implementation guarantees:
95
///
96
/// - Callbacks run on the owner executor of the `Runtime` the timers serve.
97
/// - A one-shot timer's handle reports `active()` false once the timer has fired.
98
///
99
/// - Once a handle's `cancel()` returns on the owner, its callback never runs, even when its deadline has passed
100
/// and a call is already queued.
101
/// - `cancel()` may be called from inside a callback, its own timer's included.
102
/// - A `TimerHandle` may outlive its `Scheduler`; cancelling it then does nothing.
103
/// - `every()` fires once per period while the owner runs. If the owner is blocked past several periods, a
104
/// frontend fires once and counts the next deadline from that firing: no burst of missed firings.
105
class
Scheduler {
106
public
:
107
Scheduler() =
default
;
108
virtual
~Scheduler() =
default
;
109
Scheduler(Scheduler
const
&) =
delete
;
110
Scheduler& operator=(Scheduler
const
&) =
delete
;
111
Scheduler(Scheduler&&) =
delete
;
112
Scheduler& operator=(Scheduler&&) =
delete
;
113
114
/// @brief Runs @p fn once, @p delay from now.
115
/// @param delay How long to wait; a non-positive delay means as soon as possible.
116
/// @param fn The callback.
117
/// @return The handle; destroying it before the deadline cancels the call.
118
[[nodiscard]]
virtual
TimerHandle
after
(std::chrono::milliseconds delay, std::function<
void
()> fn) = 0;
119
120
/// @brief Runs @p fn every @p period, starting one period from now.
121
/// @param period The interval; must be positive.
122
/// @param fn The callback.
123
/// @return The handle; destroying it stops the repetition.
124
/// @throws std::invalid_argument when @p period is not positive.
125
[[nodiscard]]
virtual
TimerHandle
every
(std::chrono::milliseconds period, std::function<
void
()> fn) = 0;
126
};
127
128
}
// namespace morph::reactive
morph::reactive::Scheduler::every
virtual TimerHandle every(std::chrono::milliseconds period, std::function< void()> fn)=0
Runs fn every period, starting one period from now.
morph::reactive::Scheduler::after
virtual TimerHandle after(std::chrono::milliseconds delay, std::function< void()> fn)=0
Runs fn once, delay from now.
morph::reactive::TimerHandle
Owns one scheduled timer and cancels it when destroyed. Move-only.
Definition
scheduler.hpp:19
morph::reactive::TimerHandle::TimerHandle
TimerHandle(std::function< void()> cancel, std::function< bool()> isScheduled={})
Takes ownership of a timer through the function that cancels it.
Definition
scheduler.hpp:29
morph::reactive::TimerHandle::TimerHandle
TimerHandle(TimerHandle &&other) noexcept
Takes over other's timer; other then owns none.
Definition
scheduler.hpp:40
morph::reactive::TimerHandle::active
bool active() const noexcept
Whether this handle still owns a scheduled timer.
Definition
scheduler.hpp:71
morph::reactive::TimerHandle::~TimerHandle
~TimerHandle()
Cancels the timer, if this handle still owns one.
Definition
scheduler.hpp:33
morph::reactive::TimerHandle::cancel
void cancel() noexcept
Cancels the timer, if this handle still owns one.
Definition
scheduler.hpp:56
morph::reactive::TimerHandle::TimerHandle
TimerHandle()=default
A handle that owns no timer.
morph::reactive::TimerHandle::operator=
TimerHandle & operator=(TimerHandle &&other) noexcept
Cancels this handle's timer, then takes over other's.
Definition
scheduler.hpp:46
morph
reactive
scheduler.hpp
Generated by
1.17.0