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