• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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