1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements a multiplexed timer service on top of the alarm abstraction.
32 */
33
34 #include "timer.hpp"
35
36 #include "common/as_core_type.hpp"
37 #include "common/code_utils.hpp"
38 #include "common/debug.hpp"
39 #include "common/locator_getters.hpp"
40 #include "instance/instance.hpp"
41
42 namespace ot {
43
44 //---------------------------------------------------------------------------------------------------------------------
45 // `NextFireTime`
46
NextFireTime(void)47 NextFireTime::NextFireTime(void)
48 : NextFireTime(TimerMilli::GetNow())
49 {
50 }
51
NextFireTime(Time aNow)52 NextFireTime::NextFireTime(Time aNow)
53 : mNow(aNow)
54 , mNextTime(aNow.GetDistantFuture())
55 {
56 }
57
UpdateIfEarlier(Time aTime)58 void NextFireTime::UpdateIfEarlier(Time aTime) { mNextTime = Min(mNextTime, Max(mNow, aTime)); }
59
60 //---------------------------------------------------------------------------------------------------------------------
61 // `Timer`
62
63 const Timer::Scheduler::AlarmApi TimerMilli::Scheduler::sAlarmMilliApi = {
64 &otPlatAlarmMilliStartAt,
65 &otPlatAlarmMilliStop,
66 &otPlatAlarmMilliGetNow,
67 };
68
DoesFireBefore(const Timer & aSecondTimer,Time aNow) const69 bool Timer::DoesFireBefore(const Timer &aSecondTimer, Time aNow) const
70 {
71 // Indicates whether the fire time of this timer is strictly
72 // before the fire time of a second given timer.
73
74 bool retval;
75 bool isBeforeNow = (GetFireTime() < aNow);
76
77 // Check if one timer is before `now` and the other one is not.
78 if ((aSecondTimer.GetFireTime() < aNow) != isBeforeNow)
79 {
80 // One timer is before `now` and the other one is not, so if
81 // this timer's fire time is before `now` then the second fire
82 // time would be after `now` and this timer would fire before
83 // the second timer.
84
85 retval = isBeforeNow;
86 }
87 else
88 {
89 // Both timers are before `now` or both are after `now`. Either
90 // way the difference is guaranteed to be less than `kMaxDt` so
91 // we can safely compare the fire times directly.
92
93 retval = GetFireTime() < aSecondTimer.GetFireTime();
94 }
95
96 return retval;
97 }
98
99 //---------------------------------------------------------------------------------------------------------------------
100 // `TimerMilli`
101
Start(uint32_t aDelay)102 void TimerMilli::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); }
103
StartAt(TimeMilli aStartTime,uint32_t aDelay)104 void TimerMilli::StartAt(TimeMilli aStartTime, uint32_t aDelay)
105 {
106 OT_ASSERT(aDelay <= kMaxDelay);
107 FireAt(aStartTime + aDelay);
108 }
109
FireAt(TimeMilli aFireTime)110 void TimerMilli::FireAt(TimeMilli aFireTime)
111 {
112 mFireTime = aFireTime;
113 Get<Scheduler>().Add(*this);
114 }
115
FireAt(const NextFireTime & aNextFireTime)116 void TimerMilli::FireAt(const NextFireTime &aNextFireTime)
117 {
118 if (aNextFireTime.IsSet())
119 {
120 FireAt(aNextFireTime.GetNextTime());
121 }
122 else
123 {
124 Stop();
125 }
126 }
127
FireAtIfEarlier(TimeMilli aFireTime)128 void TimerMilli::FireAtIfEarlier(TimeMilli aFireTime)
129 {
130 if (!IsRunning() || (mFireTime > aFireTime))
131 {
132 FireAt(aFireTime);
133 }
134 }
135
FireAtIfEarlier(const NextFireTime & aNextFireTime)136 void TimerMilli::FireAtIfEarlier(const NextFireTime &aNextFireTime)
137 {
138 if (aNextFireTime.IsSet())
139 {
140 FireAtIfEarlier(aNextFireTime.GetNextTime());
141 }
142 }
143
Stop(void)144 void TimerMilli::Stop(void) { Get<Scheduler>().Remove(*this); }
145
RemoveAll(Instance & aInstance)146 void TimerMilli::RemoveAll(Instance &aInstance) { aInstance.Get<Scheduler>().RemoveAll(); }
147
148 //---------------------------------------------------------------------------------------------------------------------
149 // `Timer::Scheduler`
150
Add(Timer & aTimer,const AlarmApi & aAlarmApi)151 void Timer::Scheduler::Add(Timer &aTimer, const AlarmApi &aAlarmApi)
152 {
153 Timer *prev = nullptr;
154 Time now(aAlarmApi.AlarmGetNow());
155
156 Remove(aTimer, aAlarmApi);
157
158 for (Timer &cur : mTimerList)
159 {
160 if (aTimer.DoesFireBefore(cur, now))
161 {
162 break;
163 }
164
165 prev = &cur;
166 }
167
168 if (prev == nullptr)
169 {
170 mTimerList.Push(aTimer);
171 SetAlarm(aAlarmApi);
172 }
173 else
174 {
175 mTimerList.PushAfter(aTimer, *prev);
176 }
177 }
178
Remove(Timer & aTimer,const AlarmApi & aAlarmApi)179 void Timer::Scheduler::Remove(Timer &aTimer, const AlarmApi &aAlarmApi)
180 {
181 VerifyOrExit(aTimer.IsRunning());
182
183 if (mTimerList.GetHead() == &aTimer)
184 {
185 mTimerList.Pop();
186 SetAlarm(aAlarmApi);
187 }
188 else
189 {
190 IgnoreError(mTimerList.Remove(aTimer));
191 }
192
193 aTimer.SetNext(&aTimer);
194
195 exit:
196 return;
197 }
198
SetAlarm(const AlarmApi & aAlarmApi)199 void Timer::Scheduler::SetAlarm(const AlarmApi &aAlarmApi)
200 {
201 if (mTimerList.IsEmpty())
202 {
203 aAlarmApi.AlarmStop(&GetInstance());
204 }
205 else
206 {
207 Timer *timer = mTimerList.GetHead();
208 Time now(aAlarmApi.AlarmGetNow());
209 uint32_t remaining;
210
211 remaining = (now < timer->mFireTime) ? (timer->mFireTime - now) : 0;
212
213 aAlarmApi.AlarmStartAt(&GetInstance(), now.GetValue(), remaining);
214 }
215 }
216
ProcessTimers(const AlarmApi & aAlarmApi)217 void Timer::Scheduler::ProcessTimers(const AlarmApi &aAlarmApi)
218 {
219 Timer *timer = mTimerList.GetHead();
220
221 if (timer)
222 {
223 Time now(aAlarmApi.AlarmGetNow());
224
225 if (now >= timer->mFireTime)
226 {
227 Remove(*timer, aAlarmApi); // `Remove()` will `SetAlarm` for next timer if there is any.
228 timer->Fired();
229 ExitNow();
230 }
231 }
232
233 SetAlarm(aAlarmApi);
234
235 exit:
236 return;
237 }
238
RemoveAll(const AlarmApi & aAlarmApi)239 void Timer::Scheduler::RemoveAll(const AlarmApi &aAlarmApi)
240 {
241 Timer *timer;
242
243 while ((timer = mTimerList.Pop()) != nullptr)
244 {
245 timer->SetNext(timer);
246 }
247
248 SetAlarm(aAlarmApi);
249 }
250
otPlatAlarmMilliFired(otInstance * aInstance)251 extern "C" void otPlatAlarmMilliFired(otInstance *aInstance)
252 {
253 VerifyOrExit(otInstanceIsInitialized(aInstance));
254 AsCoreType(aInstance).Get<TimerMilli::Scheduler>().ProcessTimers();
255
256 exit:
257 return;
258 }
259
260 //---------------------------------------------------------------------------------------------------------------------
261 // `TimerMicro`
262
263 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
264 const Timer::Scheduler::AlarmApi TimerMicro::Scheduler::sAlarmMicroApi = {
265 &otPlatAlarmMicroStartAt,
266 &otPlatAlarmMicroStop,
267 &otPlatAlarmMicroGetNow,
268 };
269
Start(uint32_t aDelay)270 void TimerMicro::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); }
271
StartAt(TimeMicro aStartTime,uint32_t aDelay)272 void TimerMicro::StartAt(TimeMicro aStartTime, uint32_t aDelay)
273 {
274 OT_ASSERT(aDelay <= kMaxDelay);
275 FireAt(aStartTime + aDelay);
276 }
277
FireAt(TimeMicro aFireTime)278 void TimerMicro::FireAt(TimeMicro aFireTime)
279 {
280 mFireTime = aFireTime;
281 Get<Scheduler>().Add(*this);
282 }
283
Stop(void)284 void TimerMicro::Stop(void) { Get<Scheduler>().Remove(*this); }
285
RemoveAll(Instance & aInstance)286 void TimerMicro::RemoveAll(Instance &aInstance) { aInstance.Get<Scheduler>().RemoveAll(); }
287
otPlatAlarmMicroFired(otInstance * aInstance)288 extern "C" void otPlatAlarmMicroFired(otInstance *aInstance)
289 {
290 VerifyOrExit(otInstanceIsInitialized(aInstance));
291 AsCoreType(aInstance).Get<TimerMicro::Scheduler>().ProcessTimers();
292
293 exit:
294 return;
295 }
296 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
297
298 } // namespace ot
299