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/random.hpp"
39 
40 namespace ot {
41 
TrickleTimer(Instance & aInstance,Handler aHandler)42 TrickleTimer::TrickleTimer(Instance &aInstance, Handler aHandler)
43     : TimerMilli(aInstance, TrickleTimer::HandleTimer)
44     , mIntervalMin(0)
45     , mIntervalMax(0)
46     , mInterval(0)
47     , mTimeInInterval(0)
48     , mRedundancyConstant(0)
49     , mCounter(0)
50     , mHandler(aHandler)
51     , mMode(kModeTrickle)
52     , mPhase(kBeforeRandomTime)
53 {
54 }
55 
Start(Mode aMode,uint32_t aIntervalMin,uint32_t aIntervalMax,uint16_t aRedundancyConstant)56 void TrickleTimer::Start(Mode aMode, uint32_t aIntervalMin, uint32_t aIntervalMax, uint16_t aRedundancyConstant)
57 {
58     OT_ASSERT((aIntervalMax >= aIntervalMin) && (aIntervalMin > 0));
59 
60     mIntervalMin        = aIntervalMin;
61     mIntervalMax        = aIntervalMax;
62     mRedundancyConstant = aRedundancyConstant;
63     mMode               = aMode;
64 
65     // Select interval randomly from range [Imin, Imax].
66     mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1);
67 
68     StartNewInterval();
69 }
70 
IndicateConsistent(void)71 void TrickleTimer::IndicateConsistent(void)
72 {
73     if (mCounter < kInfiniteRedundancyConstant)
74     {
75         mCounter++;
76     }
77 }
78 
IndicateInconsistent(void)79 void TrickleTimer::IndicateInconsistent(void)
80 {
81     VerifyOrExit(mMode == kModeTrickle);
82 
83     // If interval is equal to minimum when an "inconsistent" event
84     // is received, do nothing.
85 
86     VerifyOrExit(IsRunning() && (mInterval != mIntervalMin));
87 
88     mInterval = mIntervalMin;
89     StartNewInterval();
90 
91 exit:
92     return;
93 }
94 
StartNewInterval(void)95 void TrickleTimer::StartNewInterval(void)
96 {
97     uint32_t halfInterval;
98 
99     switch (mMode)
100     {
101     case kModePlainTimer:
102         mTimeInInterval = mInterval;
103         break;
104 
105     case kModeTrickle:
106         // Select a random point in the interval taken from the range [I/2, I).
107         halfInterval = mInterval / 2;
108         mTimeInInterval =
109             (halfInterval < mInterval) ? Random::NonCrypto::GetUint32InRange(halfInterval, mInterval) : halfInterval;
110         mCounter = 0;
111         mPhase   = kBeforeRandomTime;
112         break;
113     }
114 
115     TimerMilli::Start(mTimeInInterval);
116 }
117 
HandleTimer(Timer & aTimer)118 void TrickleTimer::HandleTimer(Timer &aTimer)
119 {
120     static_cast<TrickleTimer *>(&aTimer)->HandleTimer();
121 }
122 
HandleTimer(void)123 void TrickleTimer::HandleTimer(void)
124 {
125     switch (mMode)
126     {
127     case kModePlainTimer:
128         mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1);
129         StartNewInterval();
130         break;
131 
132     case kModeTrickle:
133         switch (mPhase)
134         {
135         case kBeforeRandomTime:
136             // We reached end of random `mTimeInInterval` (aka `t`)
137             // within the current interval. Trickle timer invokes
138             // handler if and only if the counter is less than the
139             // redundancy constant.
140 
141             mPhase = kAfterRandomTime;
142             TimerMilli::Start(mInterval - mTimeInInterval);
143             VerifyOrExit(mCounter < mRedundancyConstant);
144             break;
145 
146         case kAfterRandomTime:
147             // Interval has expired. Double the interval length and
148             // ensure result is below max.
149 
150             if (mInterval == 0)
151             {
152                 mInterval = 1;
153             }
154             else if (mInterval <= mIntervalMax - mInterval)
155             {
156                 mInterval *= 2;
157             }
158             else
159             {
160                 mInterval = mIntervalMax;
161             }
162 
163             StartNewInterval();
164             ExitNow(); // Exit so to not call `mHanlder`
165         }
166 
167         break;
168     }
169 
170     mHandler(*this);
171 
172 exit:
173     return;
174 }
175 
176 } // namespace ot
177