mirror of
https://github.com/espressif/openthread.git
synced 2026-08-02 00:57:47 +00:00
[mle] update Avd trickle timer interval based on router neighbor count (#9307)
This commit updates the `TrickleTimer` to allow `IntervalMax` and `IntervalMin` to be changed while the timer is running. In particular, when `IntervalMax` is changed to a value that is shorter than the current interval being used by the timer, the timer will adapt the new shorter interval and may fire immediately. A unit test `test_trickle_timer` has been added to validate the behavior of `TrickleTimer` in detail. All different scenarios where `IntervalMax` or `IntervalMin` are changed are covered by the unit test. The new mechanism to change the trickle timer `IntervalMax` is used to update the MLE Advertisement trickle timer. The `IntervalMax` is determined based on the number of router neighbors of the device with link quality 2 or better. If the device has fewer router neighbors, it will use a shorter `IntervalMax`. As new links are established with routers, the `IntervalMax` is recalculated and updated on the Advertisement trickle timer. This commit also adds a new test `test-024-mle-adv-imax-change.py` to validate updates to the `IntervalMax` value based on the number of router neighbors. Co-authored-by: David Smith <[email protected]>
This commit is contained in:
co-authored by
David Smith
parent
9b0d3db228
commit
650e30f81c
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/trickle_timer.hpp"
|
||||
|
||||
static ot::Instance *sInstance;
|
||||
|
||||
static uint32_t sNow = 0;
|
||||
static uint32_t sAlarmTime;
|
||||
static bool sAlarmOn = false;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
|
||||
|
||||
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
|
||||
{
|
||||
sAlarmOn = true;
|
||||
sAlarmTime = aT0 + aDt;
|
||||
}
|
||||
|
||||
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
namespace ot {
|
||||
|
||||
void AdvanceTime(uint32_t aDuration)
|
||||
{
|
||||
uint32_t time = sNow + aDuration;
|
||||
|
||||
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
|
||||
{
|
||||
sNow = sAlarmTime;
|
||||
otPlatAlarmMilliFired(sInstance);
|
||||
}
|
||||
|
||||
sNow = time;
|
||||
}
|
||||
|
||||
class TrickleTimerTester : public TrickleTimer
|
||||
{
|
||||
public:
|
||||
explicit TrickleTimerTester(Instance &aInstance)
|
||||
: TrickleTimer(aInstance, HandleTimerFired)
|
||||
, mDidFire(false)
|
||||
{
|
||||
}
|
||||
|
||||
Time GetFireTime(void) const { return TimerMilli::GetFireTime(); }
|
||||
uint32_t GetInterval(void) const { return TrickleTimer::mInterval; }
|
||||
uint32_t GetTimeInInterval(void) const { return TrickleTimer::mTimeInInterval; }
|
||||
|
||||
void VerifyTimerDidFire(void)
|
||||
{
|
||||
VerifyOrQuit(mDidFire);
|
||||
mDidFire = false;
|
||||
}
|
||||
|
||||
void VerifyTimerDidNotFire(void) const { VerifyOrQuit(!mDidFire); }
|
||||
|
||||
static void RemoveAll(Instance &aInstance) { TimerMilli::RemoveAll(aInstance); }
|
||||
|
||||
private:
|
||||
static void HandleTimerFired(TrickleTimer &aTimer) { static_cast<TrickleTimerTester &>(aTimer).HandleTimerFired(); }
|
||||
void HandleTimerFired(void) { mDidFire = true; }
|
||||
|
||||
bool mDidFire;
|
||||
};
|
||||
|
||||
void AlarmFired(otInstance *aInstance) { otPlatAlarmMilliFired(aInstance); }
|
||||
|
||||
void TestTrickleTimerPlainMode(void)
|
||||
{
|
||||
static constexpr uint32_t kMinInterval = 2000;
|
||||
static constexpr uint32_t kMaxInterval = 5000;
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
uint32_t interval;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerPlainMode() ");
|
||||
|
||||
// Validate that timer picks a random interval between min and max
|
||||
// on start.
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModePlainTimer, kMinInterval, kMaxInterval, 0);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
|
||||
VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
|
||||
|
||||
for (uint8_t iter = 0; iter <= 10; iter++)
|
||||
{
|
||||
AdvanceTime(interval);
|
||||
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// The plain mode trickle timer restarts with a new random
|
||||
// interval between min and max.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
|
||||
}
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestTrickleTimerTrickleMode(uint32_t aRedundancyConstant, uint32_t aConsistentCalls)
|
||||
{
|
||||
static constexpr uint32_t kMinInterval = 1000;
|
||||
static constexpr uint32_t kMaxInterval = 9000;
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
uint32_t interval;
|
||||
uint32_t t;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerTrickleMode(aRedundancyConstant:%u, aConsistentCalls:%u) ", aRedundancyConstant,
|
||||
aConsistentCalls);
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModeTrickle, kMinInterval, kMaxInterval, aRedundancyConstant);
|
||||
|
||||
// Validate that trickle timer starts with random interval between
|
||||
// min/max.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
|
||||
VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((kMinInterval <= interval) && (interval <= kMaxInterval));
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
|
||||
// After `IndicateInconsistent()` should go back to min
|
||||
// interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit(interval == kMinInterval);
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
|
||||
for (uint8_t iter = 0; iter < 10; iter++)
|
||||
{
|
||||
for (uint32_t index = 0; index < aConsistentCalls; index++)
|
||||
{
|
||||
timer.IndicateConsistent();
|
||||
}
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
if (aConsistentCalls < aRedundancyConstant)
|
||||
{
|
||||
timer.VerifyTimerDidFire();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer.VerifyTimerDidNotFire();
|
||||
}
|
||||
|
||||
AdvanceTime(interval - t);
|
||||
|
||||
// Verify that interval is doubling each time up
|
||||
// to max interval.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetInterval() == Min(interval * 2, kMaxInterval));
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
}
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit(interval == kMinInterval);
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestTrickleTimerMinMaxIntervalChange(void)
|
||||
{
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
TimeMilli fireTime;
|
||||
uint32_t interval;
|
||||
uint32_t t;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerMinMaxIntervalChange()");
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 4000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 2000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that `SetIntervalMin()` to a larger value than
|
||||
// previously set does not impact the current interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMin` before time `t`.
|
||||
|
||||
timer.SetIntervalMin(3000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMin` after time `t`.
|
||||
|
||||
timer.SetIntervalMin(3500);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3500);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that `SetIntervalMin()` to a smaller value
|
||||
// also does not impact the current interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 3500);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMin` before time `t`.
|
||||
|
||||
timer.SetIntervalMin(3000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMin` after time `t`.
|
||||
|
||||
timer.SetIntervalMin(2000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 2000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that changing `IntervalMax` to a larger value
|
||||
// than the current interval being used by timer, does not
|
||||
// impact the current internal.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMax` before time `t`.
|
||||
|
||||
timer.SetIntervalMax(2500);
|
||||
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 2500);
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMax` after time `t`.
|
||||
|
||||
timer.SetIntervalMax(3000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
timer.Stop();
|
||||
VerifyOrQuit(!timer.IsRunning());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Check behavior when the new `IntervalMax` is smaller
|
||||
// than the current interval being used by timer.
|
||||
|
||||
// New `Imax` is smaller than `t` and before now.
|
||||
//
|
||||
// |<---- interval --^-------------------------------->|
|
||||
// |<---- t ---------^------------------>| |
|
||||
// |<---- new Imax --^--->| | |
|
||||
// | now | | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(100);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
timer.SetIntervalMax(500);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == 500);
|
||||
VerifyOrQuit(timer.GetTimeInInterval() == 500);
|
||||
VerifyOrQuit(timer.GetFireTime() != fireTime);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
AdvanceTime(400);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// New `Imax` is smaller than `t` and after now.
|
||||
//
|
||||
// |<---- interval --------------^-------------------->|
|
||||
// |<---- t ---------------------^------>| |
|
||||
// |<---- new Imax ------>| ^ | |
|
||||
// | | now | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(800);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
timer.SetIntervalMax(500);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == 500);
|
||||
VerifyOrQuit(timer.GetTimeInInterval() == 500);
|
||||
VerifyOrQuit(timer.GetFireTime() != fireTime);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
AdvanceTime(0);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// New `Imax` is larger than `t` and before now.
|
||||
//
|
||||
// |<---- interval --------------------------------^-->|
|
||||
// |<---- t ---------------------------->| ^ |
|
||||
// |<---- new Imax --------------------------->| ^ |
|
||||
// | | | now |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(1999);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
timer.SetIntervalMax(t + 1);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == t + 1);
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Check that new interval is started immediately.
|
||||
AdvanceTime(0);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
VerifyOrQuit(fireTime != timer.GetFireTime());
|
||||
VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
|
||||
|
||||
// New `Imax` is larger than `t` and after now.
|
||||
//
|
||||
// |<---- interval -------------------------^--------->|
|
||||
// |<---- t ---------------------------->| ^ |
|
||||
// |<---- new Imax -------------------------^->| |
|
||||
// | | now | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
timer.SetIntervalMax(t + 1);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == t + 1);
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
AdvanceTime(1);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
VerifyOrQuit(fireTime != timer.GetFireTime());
|
||||
VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestTrickleTimerPlainMode();
|
||||
ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 5, /* aConsistentCalls */ 3);
|
||||
ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 3, /* aConsistentCalls */ 3);
|
||||
ot::TestTrickleTimerMinMaxIntervalChange();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user