1 #include <sdeventplus/clock.hpp>
2 #include <sdeventplus/types.hpp>
3 #include <sdeventplus/utility/timer.hpp>
4
5 #include <memory>
6 #include <stdexcept>
7 #include <utility>
8
9 namespace sdeventplus
10 {
11 namespace utility
12 {
13
14 template <ClockId Id>
Timer(const Event & event,Callback && callback,std::optional<Duration> interval,typename source::Time<Id>::Accuracy accuracy)15 Timer<Id>::Timer(const Event& event, Callback&& callback,
16 std::optional<Duration> interval,
17 typename source::Time<Id>::Accuracy accuracy) :
18 userdata(nullptr),
19 timeSource(event,
20 Clock<Id>(event).now() + interval.value_or(Duration::zero()),
21 accuracy, nullptr)
22 {
23 auto timerData = std::make_unique<detail::TimerData<Id>>(
24 *this, std::move(callback), interval);
25 userdata = timerData.get();
26 timerData->userdata = timerData.get();
27 auto cb = [timerData = std::move(timerData)](
28 source::Time<Id>&, typename source::Time<Id>::TimePoint) {
29 timerData->internalCallback();
30 };
31 timeSource.set_callback(std::move(cb));
32 setEnabled(interval.has_value());
33 }
34
35 template <ClockId Id>
Timer(const Timer<Id> & other,sdeventplus::internal::NoOwn)36 Timer<Id>::Timer(const Timer<Id>& other, sdeventplus::internal::NoOwn) :
37 userdata(other.userdata),
38 timeSource(other.timeSource, sdeventplus::internal::NoOwn())
39 {}
40
41 template <ClockId Id>
set_callback(Callback && callback)42 void Timer<Id>::set_callback(Callback&& callback)
43 {
44 userdata->callback = std::move(callback);
45 }
46
47 template <ClockId Id>
get_event() const48 const Event& Timer<Id>::get_event() const
49 {
50 return timeSource.get_event();
51 }
52
53 template <ClockId Id>
get_floating() const54 bool Timer<Id>::get_floating() const
55 {
56 return timeSource.get_floating();
57 }
58
59 template <ClockId Id>
set_floating(bool b)60 void Timer<Id>::set_floating(bool b)
61 {
62 return timeSource.set_floating(b);
63 }
64
65 template <ClockId Id>
hasExpired() const66 bool Timer<Id>::hasExpired() const
67 {
68 return userdata->expired;
69 }
70
71 template <ClockId Id>
isEnabled() const72 bool Timer<Id>::isEnabled() const
73 {
74 return timeSource.get_enabled() != source::Enabled::Off;
75 }
76
77 template <ClockId Id>
getInterval() const78 std::optional<typename Timer<Id>::Duration> Timer<Id>::getInterval() const
79 {
80 return userdata->interval;
81 }
82
83 template <ClockId Id>
getRemaining() const84 typename Timer<Id>::Duration Timer<Id>::getRemaining() const
85 {
86 if (!isEnabled())
87 {
88 throw std::runtime_error("Timer not running");
89 }
90
91 auto end = timeSource.get_time();
92 auto now = userdata->clock.now();
93 if (end < now)
94 {
95 return Duration{0};
96 }
97 return end - now;
98 }
99
100 template <ClockId Id>
setEnabled(bool enabled)101 void Timer<Id>::setEnabled(bool enabled)
102 {
103 if (enabled && !userdata->initialized)
104 {
105 throw std::runtime_error("Timer was never initialized");
106 }
107 timeSource.set_enabled(
108 enabled ? source::Enabled::On : source::Enabled::Off);
109 }
110
111 template <ClockId Id>
setRemaining(Duration remaining)112 void Timer<Id>::setRemaining(Duration remaining)
113 {
114 timeSource.set_time(userdata->clock.now() + remaining);
115 userdata->initialized = true;
116 }
117
118 template <ClockId Id>
resetRemaining()119 void Timer<Id>::resetRemaining()
120 {
121 setRemaining(userdata->interval.value());
122 }
123
124 template <ClockId Id>
setInterval(std::optional<Duration> interval)125 void Timer<Id>::setInterval(std::optional<Duration> interval)
126 {
127 userdata->interval = interval;
128 }
129
130 template <ClockId Id>
clearExpired()131 void Timer<Id>::clearExpired()
132 {
133 userdata->expired = false;
134 }
135
136 template <ClockId Id>
restart(std::optional<Duration> interval)137 void Timer<Id>::restart(std::optional<Duration> interval)
138 {
139 clearExpired();
140 userdata->initialized = false;
141 setInterval(interval);
142 if (userdata->interval)
143 {
144 resetRemaining();
145 }
146 setEnabled(userdata->interval.has_value());
147 }
148
149 template <ClockId Id>
restartOnce(Duration remaining)150 void Timer<Id>::restartOnce(Duration remaining)
151 {
152 clearExpired();
153 userdata->initialized = false;
154 setInterval(std::nullopt);
155 setRemaining(remaining);
156 setEnabled(true);
157 }
158
159 template <ClockId Id>
internalCallback()160 void Timer<Id>::internalCallback()
161 {
162 userdata->expired = true;
163 userdata->initialized = false;
164 if (userdata->interval)
165 {
166 resetRemaining();
167 }
168 else
169 {
170 setEnabled(false);
171 }
172
173 if (userdata->callback)
174 {
175 userdata->callback(*userdata);
176 }
177 }
178
179 template class Timer<ClockId::RealTime>;
180 template class Timer<ClockId::Monotonic>;
181 template class Timer<ClockId::BootTime>;
182 template class Timer<ClockId::RealTimeAlarm>;
183 template class Timer<ClockId::BootTimeAlarm>;
184
185 namespace detail
186 {
187
188 template <ClockId Id>
TimerData(const Timer<Id> & base,typename Timer<Id>::Callback && callback,std::optional<typename Timer<Id>::Duration> interval)189 TimerData<Id>::TimerData(const Timer<Id>& base,
190 typename Timer<Id>::Callback&& callback,
191 std::optional<typename Timer<Id>::Duration> interval) :
192 Timer<Id>(base, sdeventplus::internal::NoOwn()), expired(false),
193 initialized(interval.has_value()), callback(std::move(callback)),
194 clock(Event(base.timeSource.get_event(), sdeventplus::internal::NoOwn())),
195 interval(interval)
196 {}
197
198 } // namespace detail
199
200 } // namespace utility
201 } // namespace sdeventplus
202