diff --git a/src/core/common/trickle_timer.cpp b/src/core/common/trickle_timer.cpp index 322d2dac4..bfa3aecce 100644 --- a/src/core/common/trickle_timer.cpp +++ b/src/core/common/trickle_timer.cpp @@ -39,39 +39,28 @@ namespace ot { -TrickleTimer::TrickleTimer(Instance &aInstance, -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - uint32_t aRedundancyConstant, -#endif - Handler aTransmitHandler, - Handler aIntervalExpiredHandler) +TrickleTimer::TrickleTimer(Instance &aInstance, Handler aHandler) : TimerMilli(aInstance, TrickleTimer::HandleTimer) -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - , mRedundancyConstant(aRedundancyConstant) - , mCounter(0) -#endif , mIntervalMin(0) , mIntervalMax(0) , mInterval(0) , mTimeInInterval(0) - , mTransmitHandler(aTransmitHandler) - , mIntervalExpiredHandler(aIntervalExpiredHandler) - , mMode(kModeNormal) - , mIsRunning(false) - , mInTransmitPhase(false) + , mRedundancyConstant(0) + , mCounter(0) + , mHandler(aHandler) + , mMode(kModeTrickle) + , mPhase(kBeforeRandomTime) { - OT_ASSERT(aTransmitHandler != nullptr); } -void TrickleTimer::Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode) +void TrickleTimer::Start(Mode aMode, uint32_t aIntervalMin, uint32_t aIntervalMax, uint16_t aRedundancyConstant) { - OT_ASSERT(aIntervalMax >= aIntervalMin); - OT_ASSERT(aIntervalMin != 0 || aIntervalMax != 0); + OT_ASSERT((aIntervalMax >= aIntervalMin) && (aIntervalMin > 0)); - mIntervalMin = aIntervalMin; - mIntervalMax = aIntervalMax; - mMode = aMode; - mIsRunning = true; + mIntervalMin = aIntervalMin; + mIntervalMax = aIntervalMax; + mRedundancyConstant = aRedundancyConstant; + mMode = aMode; // Select interval randomly from range [Imin, Imax]. mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1); @@ -79,17 +68,22 @@ void TrickleTimer::Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMod StartNewInterval(); } -void TrickleTimer::Stop(void) +void TrickleTimer::IndicateConsistent(void) { - mIsRunning = false; - TimerMilli::Stop(); + if (mCounter < kInfiniteRedundancyConstant) + { + mCounter++; + } } void TrickleTimer::IndicateInconsistent(void) { + VerifyOrExit(mMode == kModeTrickle); + // If interval is equal to minimum when an "inconsistent" event // is received, do nothing. - VerifyOrExit(mIsRunning && (mInterval != mIntervalMin)); + + VerifyOrExit(IsRunning() && (mInterval != mIntervalMin)); mInterval = mIntervalMin; StartNewInterval(); @@ -102,33 +96,22 @@ void TrickleTimer::StartNewInterval(void) { uint32_t halfInterval; -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - mCounter = 0; -#endif - - mInTransmitPhase = true; - switch (mMode) { - case kModeNormal: - halfInterval = mInterval / 2; - VerifyOrExit(halfInterval < mInterval, mTimeInInterval = halfInterval); - - // Select a random point in the interval taken from the range [I/2, I). - mTimeInInterval = Random::NonCrypto::GetUint32InRange(halfInterval, mInterval); - break; - case kModePlainTimer: mTimeInInterval = mInterval; break; - case kModeMPL: - // Select a random point in interval taken from the range [0, I]. - mTimeInInterval = Random::NonCrypto::GetUint32InRange(0, mInterval + 1); + case kModeTrickle: + // Select a random point in the interval taken from the range [I/2, I). + halfInterval = mInterval / 2; + mTimeInInterval = + (halfInterval < mInterval) ? Random::NonCrypto::GetUint32InRange(halfInterval, mInterval) : halfInterval; + mCounter = 0; + mPhase = kBeforeRandomTime; break; } -exit: TimerMilli::Start(mTimeInInterval); } @@ -139,71 +122,52 @@ void TrickleTimer::HandleTimer(Timer &aTimer) void TrickleTimer::HandleTimer(void) { - if (mInTransmitPhase) - { - HandleEndOfTimeInInterval(); - } - else - { - HandleEndOfInterval(); - } -} - -void TrickleTimer::HandleEndOfTimeInInterval(void) -{ -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - // Trickle transmits if and only if the counter `c` is less - // than the redundancy constant `k`. - if (mRedundancyConstant == 0 || mCounter < mRedundancyConstant) -#endif - { - bool shouldContinue = mTransmitHandler(*this); - VerifyOrExit(shouldContinue, Stop()); - } - switch (mMode) { case kModePlainTimer: - // Select a random interval in [Imin, Imax] and restart. mInterval = Random::NonCrypto::GetUint32InRange(mIntervalMin, mIntervalMax + 1); StartNewInterval(); break; - case kModeNormal: - case kModeMPL: - // Waiting for the rest of the interval to elapse. - mInTransmitPhase = false; - TimerMilli::Start(mInterval - mTimeInInterval); + case kModeTrickle: + switch (mPhase) + { + case kBeforeRandomTime: + // We reached end of random `mTimeInInterval` (aka `t`) + // within the current interval. Trickle timer invokes + // handler if and only if the counter is less than the + // redundancy constant. + + mPhase = kAfterRandomTime; + TimerMilli::Start(mInterval - mTimeInInterval); + VerifyOrExit(mCounter < mRedundancyConstant); + break; + + case kAfterRandomTime: + // Interval has expired. Double the interval length and + // ensure result is below max. + + if (mInterval == 0) + { + mInterval = 1; + } + else if (mInterval <= mIntervalMax - mInterval) + { + mInterval *= 2; + } + else + { + mInterval = mIntervalMax; + } + + StartNewInterval(); + ExitNow(); // Exit so to not call `mHanlder` + } + break; } -exit: - return; -} - -void TrickleTimer::HandleEndOfInterval(void) -{ - // Double the interval and ensure result is below max. - if (mInterval == 0) - { - mInterval = 1; - } - else if (mInterval <= mIntervalMax - mInterval) - { - mInterval *= 2; - } - else - { - mInterval = mIntervalMax; - } - - if (mIntervalExpiredHandler) - { - bool shouldContinue = mIntervalExpiredHandler(*this); - VerifyOrExit(shouldContinue, Stop()); - } - - StartNewInterval(); + mHandler(*this); exit: return; diff --git a/src/core/common/trickle_timer.hpp b/src/core/common/trickle_timer.hpp index dda6caecc..0f65c55ab 100644 --- a/src/core/common/trickle_timer.hpp +++ b/src/core/common/trickle_timer.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" +#include "common/numeric_limits.hpp" #include "common/timer.hpp" namespace ot { @@ -61,39 +62,38 @@ public: * This enumeration defines the modes of operation for the `TrickleTimer`. * */ - enum Mode + enum Mode : uint8_t { - kModeNormal, ///< Runs the normal trickle logic (as per RFC6206). - kModePlainTimer, ///< Runs a plain timer with random interval selected between min/max intervals. - kModeMPL, ///< Runs the trickle logic modified for MPL. + kModeTrickle, ///< Operate as the normal trickle logic (as per RFC 6206). + kModePlainTimer, ///< Operate as a plain periodic timer with random interval selected within min/max intervals. + }; + + enum : uint16_t + { + /** + * Special value for redundancy constant (aka `k`) to indicate infinity (when used, it disables trickle timer's + * suppression behavior, invoking the handler callback independent of number of "consistent" events). + * + */ + kInfiniteRedundancyConstant = NumericLimits::Max(), }; /** - * This function pointer is called when the timer expires. + * This function pointer is called when the timer expires (i.e., transmission should happen). * * @param[in] aTimer A reference to the trickle timer. * - * @retval TRUE If the trickle timer should continue running. - * @retval FALSE If the trickle timer should stop running. - * */ - typedef bool (*Handler)(TrickleTimer &aTimer); + typedef void (&Handler)(TrickleTimer &aTimer); /** * This constructor initializes a `TrickleTimer` instance. * - * @param[in] aInstance A reference to the OpenThread instance. - * @param[in] aRedundancyConstant The redundancy constant for the timer, also known as `k`. - * @param[in] aTransmitHandler A pointer to a function that is called when transmission should occur. - * @param[in] aIntervalExpiredHandler An optional pointer to a function that is called when the interval expires. + * @param[in] aInstance A reference to the OpenThread instance. + * @param[in] aHandler A handler which is called when transmission should occur. * */ - TrickleTimer(Instance &aInstance, -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - uint32_t aRedundancyConstant, -#endif - Handler aTransmitHandler, - Handler aIntervalExpiredHandler); + TrickleTimer(Instance &aInstance, Handler aHandler); /** * This method indicates whether or not the trickle timer instance is running. @@ -102,60 +102,85 @@ public: * @retval FALSE If the trickle timer is not running. * */ - bool IsRunning(void) const { return mIsRunning; } + bool IsRunning(void) const { return TimerMilli::IsRunning(); } + + /** + * This method gets the current operation mode of the trickle timer. + * + * @returns The current operation mode of the timer. + * + */ + Mode GetMode(void) const { return mMode; } /** * This method starts the trickle timer. * - * @param[in] aIntervalMin The minimum interval for the timer in milliseconds. - * @param[in] aIntervalMax The maximum interval for the timer in milliseconds. - * @param[in] aMode The operating mode for the timer. + * @param[in] aMode The operation mode of timer (trickle or plain periodic mode). + * @param[in] aIntervalMin The minimum interval for the timer in milliseconds. + * @param[in] aIntervalMax The maximum interval for the timer in milliseconds. + * @param[in] aRedundancyConstant The redundancy constant for the timer, also known as `k`. The default value + * is set to `kInfiniteRedundancyConstant` which disables the suppression behavior + * (i.e., handler is always invoked independent of number of "consistent" events). * */ - void Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode); + void Start(Mode aMode, + uint32_t aIntervalMin, + uint32_t aIntervalMax, + uint16_t aRedundancyConstant = kInfiniteRedundancyConstant); /** * This method stops the trickle timer. * */ - void Stop(void); + void Stop(void) { TimerMilli::Stop(); } -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT /** - * This method indicates to the trickle timer a 'consistent' state. + * This method indicates to the trickle timer a 'consistent' event. + * + * The 'consistent' events are used to control suppression behavior. The trickle timer keeps track of the number of + * 'consistent' events in each interval. The timer handler is invoked only if the number of `consistent` events + * received in the interval is less than the redundancy constant. * */ - void IndicateConsistent(void) { mCounter++; } -#endif + void IndicateConsistent(void); /** - * This method indicates to the trickle timer an 'inconsistent' state. + * This method indicates to the trickle timer an 'inconsistent' event. + * + * Receiving an 'inconsistent' event causes the trickle timer to reset (i.e., start with interval set to the min + * value) unless the current interval being used is already equal to the min interval. * */ void IndicateInconsistent(void); private: + enum Phase : uint8_t + { + kBeforeRandomTime, // Trickle timer is before random time `t` in the current interval. + kAfterRandomTime, // Trickle timer is after random time `t` in the current interval. + }; + void StartNewInterval(void); static void HandleTimer(Timer &aTimer); void HandleTimer(void); void HandleEndOfTimeInInterval(void); void HandleEndOfInterval(void); - void StartAt(void) {} // Shadow base class `TimerMilli` method to ensure it is hidden. -#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT - const uint32_t mRedundancyConstant; // Redundancy constant (aka 'k'). - uint32_t mCounter; // A counter for number of "consistent" transmissions (aka 'c'). -#endif + // Shadow base class `TimerMilli` methods to ensure they are hidden. + void StartAt(void) {} + void FireAt(void) {} + void FireAtIfEarlier(void) {} + void GetFireTime(void) {} - uint32_t mIntervalMin; // Minimum interval (aka `Imin`). - uint32_t mIntervalMax; // Maximum interval (aka `Imax`). - uint32_t mInterval; // Current interval (aka `I`). - uint32_t mTimeInInterval; // Time in interval (aka `t`). - Handler mTransmitHandler; // Transmit handler callback. - Handler mIntervalExpiredHandler; // Interval expired handler callback. - Mode mMode; // Trickle timer mode. - bool mIsRunning : 1; // Indicates if the trickle timer is running. - bool mInTransmitPhase : 1; // Indicates if in transmit phase (before time `t` in current interval `I`). + uint32_t mIntervalMin; // Minimum interval (aka `Imin`). + uint32_t mIntervalMax; // Maximum interval (aka `Imax`). + uint32_t mInterval; // Current interval (aka `I`). + uint32_t mTimeInInterval; // Time in interval (aka `t`). + uint16_t mRedundancyConstant; // Redundancy constant (aka 'k'). + uint16_t mCounter; // A counter for number of "consistent" transmissions (aka 'c'). + Handler mHandler; // Handler callback. + Mode mMode; // Trickle timer operation mode. + Phase mPhase; // Trickle timer phase (before or after time `t` in the current interval). }; /** diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp index 3b625d49c..4a617033f 100644 --- a/src/core/net/dhcp6_client.cpp +++ b/src/core/net/dhcp6_client.cpp @@ -50,7 +50,7 @@ namespace Dhcp6 { Client::Client(Instance &aInstance) : InstanceLocator(aInstance) , mSocket(aInstance) - , mTrickleTimer(aInstance, Client::HandleTrickleTimer, nullptr) + , mTrickleTimer(aInstance, Client::HandleTrickleTimer) , mStartTime(0) , mIdentityAssociationCurrent(nullptr) { @@ -203,8 +203,8 @@ bool Client::ProcessNextIdentityAssociation(void) mIdentityAssociationCurrent = &idAssociation; - mTrickleTimer.Start(Time::SecToMsec(kTrickleTimerImin), Time::SecToMsec(kTrickleTimerImax), - TrickleTimer::kModeNormal); + mTrickleTimer.Start(TrickleTimer::kModeTrickle, Time::SecToMsec(kTrickleTimerImin), + Time::SecToMsec(kTrickleTimerImax)); mTrickleTimer.IndicateInconsistent(); @@ -215,18 +215,16 @@ exit: return rval; } -bool Client::HandleTrickleTimer(TrickleTimer &aTrickleTimer) +void Client::HandleTrickleTimer(TrickleTimer &aTrickleTimer) { - return aTrickleTimer.Get().HandleTrickleTimer(); + aTrickleTimer.Get().HandleTrickleTimer(); } -bool Client::HandleTrickleTimer(void) +void Client::HandleTrickleTimer(void) { - bool rval = true; - OT_ASSERT(mSocket.IsBound()); - VerifyOrExit(mIdentityAssociationCurrent != nullptr, rval = false); + VerifyOrExit(mIdentityAssociationCurrent != nullptr, mTrickleTimer.Stop()); switch (mIdentityAssociationCurrent->mStatus) { @@ -246,7 +244,7 @@ bool Client::HandleTrickleTimer(void) if (!ProcessNextIdentityAssociation()) { Stop(); - rval = false; + mTrickleTimer.Stop(); } break; @@ -256,7 +254,7 @@ bool Client::HandleTrickleTimer(void) } exit: - return rval; + return; } void Client::Solicit(uint16_t aRloc16) diff --git a/src/core/net/dhcp6_client.hpp b/src/core/net/dhcp6_client.hpp index 3fcbaa46a..cb298803b 100644 --- a/src/core/net/dhcp6_client.hpp +++ b/src/core/net/dhcp6_client.hpp @@ -140,8 +140,8 @@ private: Error ProcessStatusCode(Message &aMessage, uint16_t aOffset); Error ProcessIaAddress(Message &aMessage, uint16_t aOffset); - static bool HandleTrickleTimer(TrickleTimer &aTrickleTimer); - bool HandleTrickleTimer(void); + static void HandleTrickleTimer(TrickleTimer &aTrickleTimer); + void HandleTrickleTimer(void); Ip6::Udp::Socket mSocket; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 38f46ad93..d696a079e 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -528,7 +528,7 @@ Error Mle::BecomeChild(AttachMode aMode) #if OPENTHREAD_FTD if (IsFullThreadDevice()) { - Get().StopAdvertiseTimer(); + Get().StopAdvertiseTrickleTimer(); } #endif } diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 8139dda4a..06853d4a8 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -56,7 +56,7 @@ namespace Mle { MleRouter::MleRouter(Instance &aInstance) : Mle(aInstance) - , mAdvertiseTimer(aInstance, MleRouter::HandleAdvertiseTimer, nullptr) + , mAdvertiseTrickleTimer(aInstance, MleRouter::HandleAdvertiseTrickleTimer) , mAddressSolicit(UriPath::kAddressSolicit, &MleRouter::HandleAddressSolicit, this) , mAddressRelease(UriPath::kAddressRelease, &MleRouter::HandleAddressRelease, this) , mChildTable(aInstance) @@ -225,7 +225,7 @@ void MleRouter::StopLeader(void) Get().RemoveResource(mAddressRelease); Get().StopLeader(); Get().StopLeader(); - StopAdvertiseTimer(); + StopAdvertiseTrickleTimer(); Get().Stop(); Get().UnsubscribeAllRoutersMulticast(); } @@ -324,7 +324,7 @@ void MleRouter::SetStateRouter(uint16_t aRloc16) mAttachCounter = 0; mAttachTimer.Stop(); mMessageTransmissionTimer.Stop(); - StopAdvertiseTimer(); + StopAdvertiseTrickleTimer(); ResetAdvertiseInterval(); Get().SubscribeAllRoutersMulticast(); @@ -355,7 +355,7 @@ void MleRouter::SetStateLeader(uint16_t aRloc16) mAttachCounter = 0; mAttachTimer.Stop(); mMessageTransmissionTimer.Stop(); - StopAdvertiseTimer(); + StopAdvertiseTrickleTimer(); ResetAdvertiseInterval(); IgnoreError(GetLeaderAloc(mLeaderAloc.GetAddress())); Get().AddUnicastAddress(mLeaderAloc); @@ -386,39 +386,37 @@ void MleRouter::SetStateLeader(uint16_t aRloc16) otLogNoteMle("Leader partition id 0x%x", mLeaderData.GetPartitionId()); } -bool MleRouter::HandleAdvertiseTimer(TrickleTimer &aTimer) +void MleRouter::HandleAdvertiseTrickleTimer(TrickleTimer &aTimer) { - return aTimer.Get().HandleAdvertiseTimer(); + aTimer.Get().HandleAdvertiseTrickleTimer(); } -bool MleRouter::HandleAdvertiseTimer(void) +void MleRouter::HandleAdvertiseTrickleTimer(void) { - bool continueTrickle = true; - - VerifyOrExit(IsRouterEligible(), continueTrickle = false); + VerifyOrExit(IsRouterEligible(), mAdvertiseTrickleTimer.Stop()); SendAdvertisement(); exit: - return continueTrickle; + return; } -void MleRouter::StopAdvertiseTimer(void) +void MleRouter::StopAdvertiseTrickleTimer(void) { - mAdvertiseTimer.Stop(); + mAdvertiseTrickleTimer.Stop(); } void MleRouter::ResetAdvertiseInterval(void) { VerifyOrExit(IsRouterOrLeader()); - if (!mAdvertiseTimer.IsRunning()) + if (!mAdvertiseTrickleTimer.IsRunning()) { - mAdvertiseTimer.Start(Time::SecToMsec(kAdvertiseIntervalMin), Time::SecToMsec(kAdvertiseIntervalMax), - TrickleTimer::kModeNormal); + mAdvertiseTrickleTimer.Start(TrickleTimer::kModeTrickle, Time::SecToMsec(kAdvertiseIntervalMin), + Time::SecToMsec(kAdvertiseIntervalMax)); } - mAdvertiseTimer.IndicateInconsistent(); + mAdvertiseTrickleTimer.IndicateInconsistent(); exit: return; @@ -1727,13 +1725,12 @@ void MleRouter::HandleTimeTick(void) InformPreviousChannel(); } - if (!mAdvertiseTimer.IsRunning()) + if (!mAdvertiseTrickleTimer.IsRunning()) { SendAdvertisement(); - mAdvertiseTimer.Start(Time::SecToMsec(kReedAdvertiseInterval), - Time::SecToMsec(kReedAdvertiseInterval + kReedAdvertiseJitter), - TrickleTimer::kModePlainTimer); + mAdvertiseTrickleTimer.Start(TrickleTimer::kModePlainTimer, Time::SecToMsec(kReedAdvertiseInterval), + Time::SecToMsec(kReedAdvertiseInterval + kReedAdvertiseJitter)); } ExitNow(); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index c2224b76f..ffb9ea6a9 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -599,7 +599,7 @@ private: #endif Error ProcessRouteTlv(const RouteTlv &aRoute); - void StopAdvertiseTimer(void); + void StopAdvertiseTrickleTimer(void); Error SendAddressSolicit(ThreadStatusTlv::Status aStatus); void SendAddressRelease(void); void SendAddressSolicitResponse(const Coap::Message & aRequest, @@ -653,11 +653,11 @@ private: bool HasOneNeighborWithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId); bool HasSmallNumberOfChildren(void); - static bool HandleAdvertiseTimer(TrickleTimer &aTimer); - bool HandleAdvertiseTimer(void); + static void HandleAdvertiseTrickleTimer(TrickleTimer &aTimer); + void HandleAdvertiseTrickleTimer(void); void HandleTimeTick(void); - TrickleTimer mAdvertiseTimer; + TrickleTimer mAdvertiseTrickleTimer; Coap::Resource mAddressSolicit; Coap::Resource mAddressRelease;