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(enabled ? source::Enabled::On
108                                    : 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()),
193     expired(false), initialized(interval.has_value()),
194     callback(std::move(callback)),
195     clock(Event(base.timeSource.get_event(), sdeventplus::internal::NoOwn())),
196     interval(interval)
197 {}
198 
199 } // namespace detail
200 
201 } // namespace utility
202 } // namespace sdeventplus
203