• 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 "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