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