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 the trickle timer logic.
32 */
33
34 #include "trickle_timer.hpp"
35
36 #include "common/code_utils.hpp"
37 #include "common/debug.hpp"
38 #include "common/num_utils.hpp"
39 #include "common/random.hpp"
40
41 namespace ot {
42
TrickleTimer(Instance & aInstance,Handler aHandler)43 TrickleTimer::TrickleTimer(Instance &aInstance, Handler aHandler)
44 : TimerMilli(aInstance, TrickleTimer::HandleTimer)
45 , mIntervalMin(0)
46 , mIntervalMax(0)
47 , mInterval(0)
48 , mTimeInInterval(0)
49 , mRedundancyConstant(0)
50 , mCounter(0)
51 , mHandler(aHandler)
52 , mMode(kModeTrickle)
53 , mPhase(kBeforeRandomTime)
54 {
55 }
56
GetStartTimeOfCurrentInterval(void) const57 TimeMilli TrickleTimer::GetStartTimeOfCurrentInterval(void) const
58 {
59 // Determines and returns the start time of the current
60 // interval.
61
62 TimeMilli startTime = TimerMilli::GetFireTime();
63
64 if (mMode == kModePlainTimer)
65 {
66 startTime -= mInterval;
67 ExitNow();
68 }
69
70 switch (mPhase)
71 {
72 case kBeforeRandomTime:
73 startTime -= mTimeInInterval;
74 break;
75
76 case kAfterRandomTime:
77 startTime -= mInterval;
78 break;
79 }
80
81 exit:
82 return startTime;
83 }
84
SetIntervalMin(uint32_t aIntervalMin)85 void TrickleTimer::SetIntervalMin(uint32_t aIntervalMin)
86 {
87 VerifyOrExit(IsRunning());
88
89 mIntervalMin = aIntervalMin;
90
91 if (mIntervalMax < mIntervalMin)
92 {
93 SetIntervalMax(mIntervalMin);
94 }
95
96 exit:
97 return;
98 }
99
SetIntervalMax(uint32_t aIntervalMax)100 void TrickleTimer::SetIntervalMax(uint32_t aIntervalMax)
101 {
102 TimeMilli endOfInterval;
103
104 VerifyOrExit(IsRunning());
105
106 aIntervalMax = Max(mIntervalMin, aIntervalMax);
107 VerifyOrExit(aIntervalMax != mIntervalMax);
108
109 mIntervalMax = aIntervalMax;
110
111 // If the new `aIntervalMax` is greater than the current
112 // `mInterval`, no action is needed. The new `aIntervalMax` will be
113 // used as the `mInterval` grows.
114
115 VerifyOrExit(mIntervalMax < mInterval);
116
117 // Determine the end of the interval as if the new and shorter
118 // `mIntervalMax` would have been used. The calculated time may
119 // be in the past. In this case, `FireAt(endOfInterval)` will
120 // cause the timer to fire immediately.
121
122 endOfInterval = GetStartTimeOfCurrentInterval() + mIntervalMax;
123
124 if (mMode == kModePlainTimer)
125 {
126 TimerMilli::FireAt(endOfInterval);
127 ExitNow();
128 }
129
130 // Trickle mode possible scenarios:
131 //
132 // We are in `kBeforeRandomTime` phase.
133 //
134 // a) If the new `aIntervalMax < mTimeInInterval`:
135 // - Reschedule the timer to fire at new `endOfInterval`
136 // (which may fire immediately).
137 // - Set `mTimeInInterval = aIntervalMax`
138 // - Set `mInterval` to use the shorter `aIntervalMax`.
139 //
140 // |<---- mInterval ----------------^------------------>|
141 // |<---- mTimeInInterval ----------^---->| |
142 // |<---- aIntervalMax -------->| ^ | |
143 // | | now | |
144 //
145 // b) If the new `aIntervalMax >= mTimeInInterval`:
146 // - Keep timer unchanged to fire at `mTimeInInterval`
147 // - Keep `mTimeInInterval` unchanged.
148 // - Set `mInterval` to use the shorter `aIntervalMax`.
149 //
150 // |<---- mInterval ----------------^------------------>|
151 // |<---- mTimeInInterval ----------^---->| | |
152 // |<---- aIntervalMax -------------^--------->| |
153 // | now | |
154 //
155 // We are in `kAfterRandomTime` phase.
156 //
157 // c) If the new `aIntervalMax < mTimeInInterval`:
158 // - Act as if current interval is already finished.
159 // - Reschedule the timer to fire at new `endOfInterval`
160 // (which should fire immediately).
161 // - Set `mInterval` to use the shorter `aIntervalMax`.
162 // - The `mTimeInInterval` value does not matter as we
163 // are in `kAfterRandomTime` phase. Keep it unchanged.
164 //
165 // |<---- mInterval ---------------------------^------->|
166 // |<---- mTimeInInterval --------------->| ^ |
167 // |<---- aIntervalMax -------->| | ^ |
168 // | | | now |
169 //
170 // d) If the new `aIntervalMax >= mTimeInInterval`:
171 // - Reschedule the timer to fire at new `endOfInterval`
172 // - Set `mInterval` to use the shorter `aIntervalMax`.
173 // - The `mTimeInInterval` value does not matter as we
174 // are in `kAfterRandomTime` phase. Keep it unchanged.
175 //
176 // |<---- mInterval ---------------------------^------->|
177 // |<---- mTimeInInterval --------------->| ^ | |
178 // |<---- aIntervalMax ------------------------^-->| |
179 // | | now | |
180
181 // In all cases we need to set `mInterval` to the new
182 // shorter `aIntervalMax`.
183
184 mInterval = aIntervalMax;
185
186 switch (mPhase)
187 {
188 case kBeforeRandomTime:
189 if (aIntervalMax < mTimeInInterval)
190 {
191 mTimeInInterval = aIntervalMax;
192 }
193 else
194 {
195 break;
196 }
197
198 OT_FALL_THROUGH;
199
200 case kAfterRandomTime:
201 TimerMilli::FireAt(endOfInterval);
202 break;
203 }
204
205 exit:
206 return;
207 }
208
Start(Mode aMode,uint32_t aIntervalMin,uint32_t aIntervalMax,uint16_t aRedundancyConstant)209 void TrickleTimer::Start(Mode aMode, uint32_t aIntervalMin, uint32_t aIntervalMax, uint16_t aRedundancyConstant)
210 {
211 OT_ASSERT((aIntervalMax >= aIntervalMin) && (aIntervalMin > 0));
212
213 mIntervalMin = aIntervalMin;
214 mIntervalMax = aIntervalMax;
215 mRedundancyConstant = aRedundancyConstant;
216 mMode = aMode;
217
218 // Select interval randomly from range [Imin, Imax].
219 mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1);
220
221 StartNewInterval();
222 }
223
IndicateConsistent(void)224 void TrickleTimer::IndicateConsistent(void)
225 {
226 if (mCounter < kInfiniteRedundancyConstant)
227 {
228 mCounter++;
229 }
230 }
231
IndicateInconsistent(void)232 void TrickleTimer::IndicateInconsistent(void)
233 {
234 VerifyOrExit(mMode == kModeTrickle);
235
236 // If interval is equal to minimum when an "inconsistent" event
237 // is received, do nothing.
238
239 VerifyOrExit(IsRunning() && (mInterval != mIntervalMin));
240
241 mInterval = mIntervalMin;
242 StartNewInterval();
243
244 exit:
245 return;
246 }
247
StartNewInterval(void)248 void TrickleTimer::StartNewInterval(void)
249 {
250 uint32_t halfInterval;
251
252 switch (mMode)
253 {
254 case kModePlainTimer:
255 mTimeInInterval = mInterval;
256 break;
257
258 case kModeTrickle:
259 // Select a random point in the interval taken from the range [I/2, I).
260 halfInterval = mInterval / 2;
261 mTimeInInterval =
262 (halfInterval < mInterval) ? Random::NonCrypto::GetUint32InRange(halfInterval, mInterval) : halfInterval;
263 mCounter = 0;
264 mPhase = kBeforeRandomTime;
265 break;
266 }
267
268 TimerMilli::Start(mTimeInInterval);
269 }
270
HandleTimer(Timer & aTimer)271 void TrickleTimer::HandleTimer(Timer &aTimer) { static_cast<TrickleTimer *>(&aTimer)->HandleTimer(); }
272
HandleTimer(void)273 void TrickleTimer::HandleTimer(void)
274 {
275 switch (mMode)
276 {
277 case kModePlainTimer:
278 mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1);
279 StartNewInterval();
280 break;
281
282 case kModeTrickle:
283 switch (mPhase)
284 {
285 case kBeforeRandomTime:
286 // We reached end of random `mTimeInInterval` (aka `t`)
287 // within the current interval. Trickle timer invokes
288 // handler if and only if the counter is less than the
289 // redundancy constant.
290
291 mPhase = kAfterRandomTime;
292 TimerMilli::Start(mInterval - mTimeInInterval);
293 VerifyOrExit(mCounter < mRedundancyConstant);
294 break;
295
296 case kAfterRandomTime:
297 // Interval has expired. Double the interval length and
298 // ensure result is below max.
299
300 if (mInterval == 0)
301 {
302 mInterval = 1;
303 }
304 else if (mInterval <= mIntervalMax - mInterval)
305 {
306 mInterval *= 2;
307 }
308 else
309 {
310 mInterval = mIntervalMax;
311 }
312
313 StartNewInterval();
314 ExitNow(); // Exit so to not call `mHandler`
315 }
316
317 break;
318 }
319
320 mHandler(*this);
321
322 exit:
323 return;
324 }
325
326 } // namespace ot
327