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 "instance/instance.hpp"
37
38 namespace ot {
39
40 //---------------------------------------------------------------------------------------------------------------------
41 // `NextFireTime`
42
NextFireTime(void)43 NextFireTime::NextFireTime(void)
44 : NextFireTime(TimerMilli::GetNow())
45 {
46 }
47
NextFireTime(Time aNow)48 NextFireTime::NextFireTime(Time aNow)
49 : mNow(aNow)
50 , mNextTime(aNow.GetDistantFuture())
51 {
52 }
53
UpdateIfEarlier(Time aTime)54 void NextFireTime::UpdateIfEarlier(Time aTime) { mNextTime = Min(mNextTime, Max(mNow, aTime)); }
55
UpdateIfEarlierAndInFuture(Time aTime)56 void NextFireTime::UpdateIfEarlierAndInFuture(Time aTime)
57 {
58 if (aTime > mNow)
59 {
60 mNextTime = Min(mNextTime, aTime);
61 }
62 }
63
64 //---------------------------------------------------------------------------------------------------------------------
65 // `Timer`
66
67 const Timer::Scheduler::AlarmApi TimerMilli::Scheduler::sAlarmMilliApi = {
68 &otPlatAlarmMilliStartAt,
69 &otPlatAlarmMilliStop,
70 &otPlatAlarmMilliGetNow,
71 };
72
DoesFireBefore(const Timer & aSecondTimer,Time aNow) const73 bool Timer::DoesFireBefore(const Timer &aSecondTimer, Time aNow) const
74 {
75 // Indicates whether the fire time of this timer is strictly
76 // before the fire time of a second given timer.
77
78 bool retval;
79 bool isBeforeNow = (GetFireTime() < aNow);
80
81 // Check if one timer is before `now` and the other one is not.
82 if ((aSecondTimer.GetFireTime() < aNow) != isBeforeNow)
83 {
84 // One timer is before `now` and the other one is not, so if
85 // this timer's fire time is before `now` then the second fire
86 // time would be after `now` and this timer would fire before
87 // the second timer.
88
89 retval = isBeforeNow;
90 }
91 else
92 {
93 // Both timers are before `now` or both are after `now`. Either
94 // way the difference is guaranteed to be less than `kMaxDt` so
95 // we can safely compare the fire times directly.
96
97 retval = GetFireTime() < aSecondTimer.GetFireTime();
98 }
99
100 return retval;
101 }
102
103 //---------------------------------------------------------------------------------------------------------------------
104 // `TimerMilli`
105
Start(uint32_t aDelay)106 void TimerMilli::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); }
107
StartAt(TimeMilli aStartTime,uint32_t aDelay)108 void TimerMilli::StartAt(TimeMilli aStartTime, uint32_t aDelay)
109 {
110 OT_ASSERT(aDelay <= kMaxDelay);
111 FireAt(aStartTime + aDelay);
112 }
113
FireAt(TimeMilli aFireTime)114 void TimerMilli::FireAt(TimeMilli aFireTime)
115 {
116 mFireTime = aFireTime;
117 Get<Scheduler>().Add(*this);
118 }
119
FireAt(const NextFireTime & aNextFireTime)120 void TimerMilli::FireAt(const NextFireTime &aNextFireTime)
121 {
122 if (aNextFireTime.IsSet())
123 {
124 FireAt(aNextFireTime.GetNextTime());
125 }
126 else
127 {
128 Stop();
129 }
130 }
131
FireAtIfEarlier(TimeMilli aFireTime)132 void TimerMilli::FireAtIfEarlier(TimeMilli aFireTime)
133 {
134 if (!IsRunning() || (mFireTime > aFireTime))
135 {
136 FireAt(aFireTime);
137 }
138 }
139
FireAtIfEarlier(const NextFireTime & aNextFireTime)140 void TimerMilli::FireAtIfEarlier(const NextFireTime &aNextFireTime)
141 {
142 if (aNextFireTime.IsSet())
143 {
144 FireAtIfEarlier(aNextFireTime.GetNextTime());
145 }
146 }
147
Stop(void)148 void TimerMilli::Stop(void) { Get<Scheduler>().Remove(*this); }
149
RemoveAll(Instance & aInstance)150 void TimerMilli::RemoveAll(Instance &aInstance) { aInstance.Get<Scheduler>().RemoveAll(); }
151
152 //---------------------------------------------------------------------------------------------------------------------
153 // `Timer::Scheduler`
154
Add(Timer & aTimer,const AlarmApi & aAlarmApi)155 void Timer::Scheduler::Add(Timer &aTimer, const AlarmApi &aAlarmApi)
156 {
157 Timer *prev = nullptr;
158 Time now(aAlarmApi.AlarmGetNow());
159
160 Remove(aTimer, aAlarmApi);
161
162 for (Timer &cur : mTimerList)
163 {
164 if (aTimer.DoesFireBefore(cur, now))
165 {
166 break;
167 }
168
169 prev = &cur;
170 }
171
172 if (prev == nullptr)
173 {
174 mTimerList.Push(aTimer);
175 SetAlarm(aAlarmApi);
176 }
177 else
178 {
179 mTimerList.PushAfter(aTimer, *prev);
180 }
181 }
182
Remove(Timer & aTimer,const AlarmApi & aAlarmApi)183 void Timer::Scheduler::Remove(Timer &aTimer, const AlarmApi &aAlarmApi)
184 {
185 VerifyOrExit(aTimer.IsRunning());
186
187 if (mTimerList.GetHead() == &aTimer)
188 {
189 mTimerList.Pop();
190 SetAlarm(aAlarmApi);
191 }
192 else
193 {
194 IgnoreError(mTimerList.Remove(aTimer));
195 }
196
197 aTimer.SetNext(&aTimer);
198
199 exit:
200 return;
201 }
202
SetAlarm(const AlarmApi & aAlarmApi)203 void Timer::Scheduler::SetAlarm(const AlarmApi &aAlarmApi)
204 {
205 if (mTimerList.IsEmpty())
206 {
207 aAlarmApi.AlarmStop(&GetInstance());
208 }
209 else
210 {
211 Timer *timer = mTimerList.GetHead();
212 Time now(aAlarmApi.AlarmGetNow());
213 uint32_t remaining;
214
215 remaining = (now < timer->mFireTime) ? (timer->mFireTime - now) : 0;
216
217 aAlarmApi.AlarmStartAt(&GetInstance(), now.GetValue(), remaining);
218 }
219 }
220
ProcessTimers(const AlarmApi & aAlarmApi)221 void Timer::Scheduler::ProcessTimers(const AlarmApi &aAlarmApi)
222 {
223 Timer *timer = mTimerList.GetHead();
224
225 if (timer)
226 {
227 Time now(aAlarmApi.AlarmGetNow());
228
229 if (now >= timer->mFireTime)
230 {
231 Remove(*timer, aAlarmApi); // `Remove()` will `SetAlarm` for next timer if there is any.
232 timer->Fired();
233 ExitNow();
234 }
235 }
236
237 SetAlarm(aAlarmApi);
238
239 exit:
240 return;
241 }
242
RemoveAll(const AlarmApi & aAlarmApi)243 void Timer::Scheduler::RemoveAll(const AlarmApi &aAlarmApi)
244 {
245 Timer *timer;
246
247 while ((timer = mTimerList.Pop()) != nullptr)
248 {
249 timer->SetNext(timer);
250 }
251
252 SetAlarm(aAlarmApi);
253 }
254
otPlatAlarmMilliFired(otInstance * aInstance)255 extern "C" void otPlatAlarmMilliFired(otInstance *aInstance)
256 {
257 VerifyOrExit(otInstanceIsInitialized(aInstance));
258 AsCoreType(aInstance).Get<TimerMilli::Scheduler>().ProcessTimers();
259
260 exit:
261 return;
262 }
263
264 //---------------------------------------------------------------------------------------------------------------------
265 // `TimerMicro`
266
267 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
268 const Timer::Scheduler::AlarmApi TimerMicro::Scheduler::sAlarmMicroApi = {
269 &otPlatAlarmMicroStartAt,
270 &otPlatAlarmMicroStop,
271 &otPlatAlarmMicroGetNow,
272 };
273
Start(uint32_t aDelay)274 void TimerMicro::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); }
275
StartAt(TimeMicro aStartTime,uint32_t aDelay)276 void TimerMicro::StartAt(TimeMicro aStartTime, uint32_t aDelay)
277 {
278 OT_ASSERT(aDelay <= kMaxDelay);
279 FireAt(aStartTime + aDelay);
280 }
281
FireAt(TimeMicro aFireTime)282 void TimerMicro::FireAt(TimeMicro aFireTime)
283 {
284 mFireTime = aFireTime;
285 Get<Scheduler>().Add(*this);
286 }
287
Stop(void)288 void TimerMicro::Stop(void) { Get<Scheduler>().Remove(*this); }
289
RemoveAll(Instance & aInstance)290 void TimerMicro::RemoveAll(Instance &aInstance) { aInstance.Get<Scheduler>().RemoveAll(); }
291
otPlatAlarmMicroFired(otInstance * aInstance)292 extern "C" void otPlatAlarmMicroFired(otInstance *aInstance)
293 {
294 VerifyOrExit(otInstanceIsInitialized(aInstance));
295 AsCoreType(aInstance).Get<TimerMicro::Scheduler>().ProcessTimers();
296
297 exit:
298 return;
299 }
300 #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
301
302 } // namespace ot
303