Implement TrickleTimer class (#603)

* Initial trickle timer implementation
This commit is contained in:
Nick Banks
2016-09-16 20:19:31 -07:00
committed by Jonathan Hui
parent 0071edcf7a
commit e74bee468b
5 changed files with 446 additions and 64 deletions
+2
View File
@@ -43,6 +43,7 @@ libopenthread_a_SOURCES = \
common/message.cpp \
common/tasklet.cpp \
common/timer.cpp \
common/trickle_timer.cpp \
crypto/aes_ccm.cpp \
crypto/aes_ecb.cpp \
crypto/hmac_sha256.cpp \
@@ -115,6 +116,7 @@ noinst_HEADERS = \
common/new.hpp \
common/tasklet.hpp \
common/timer.hpp \
common/trickle_timer.hpp \
crypto/aes_ccm.hpp \
crypto/aes_ecb.hpp \
crypto/hmac_sha256.hpp \
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (c) 2016, 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.
*/
/**
* @file
* This file implements the trickle timer logic.
*/
#include <common/code_utils.hpp>
#include <common/debug.hpp>
#include <common/trickle_timer.hpp>
#include <platform/random.h>
namespace Thread {
TrickleTimer::TrickleTimer(
TimerScheduler &aScheduler,
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
uint32_t aRedundancyConstant,
#endif
Handler aTransmitHandler, Handler aIntervalExpiredHandler, void *aContext)
:
mTimer(aScheduler, HandleTimerFired, this),
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
k(aRedundancyConstant),
#endif
mPhase(kPhaseDormant),
mTransmitHandler(aTransmitHandler),
mIntervalExpiredHandler(aIntervalExpiredHandler),
mContext(aContext)
{
}
bool TrickleTimer::IsRunning(void) const
{
return mPhase != kPhaseDormant;
}
void TrickleTimer::Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode)
{
assert(!IsRunning());
// Set the interval limits and mode
Imin = aIntervalMin;
Imax = aIntervalMax;
mMode = aMode;
// Initialize I to [Imin, Imax]
I = Imin + otPlatRandomGet() % (Imax - Imin);
// Start a new interval
StartNewInterval();
}
void TrickleTimer::Stop(void)
{
mPhase = kPhaseDormant;
mTimer.Stop();
}
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
void TrickleTimer::IndicateConsistent(void)
{
// Increment counter
c++;
}
#endif
void TrickleTimer::IndicateInconsistent(void)
{
// Only relavent if we aren't already at 'I' == 'Imin'
if (IsRunning() && I != Imin)
{
// Reset I to Imin
I = Imin;
// Stop the existing timer
mTimer.Stop();
// Start a new interval
StartNewInterval();
}
}
void TrickleTimer::StartNewInterval(void)
{
// Reset the counter and timer phase
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
c = 0;
#endif
mPhase = kPhaseTransmit;
// Initialize t
if (I == 0)
{
// Immediate interval, just set t to 0
t = 0;
}
else if (mMode == kModeMPL)
{
// Initialize t to random value between (0, I]
t = otPlatRandomGet() % I;
}
else if (mMode == kModePlainTimer)
{
// Initialize t to I, which has already been randomized in Start
t = I;
}
else
{
// Initialize t to random value between (I/2, I]
t = (I / 2) + otPlatRandomGet() % (I / 2);
}
// Start the timer for 't' milliseconds from now
mTimer.Start(t);
}
void TrickleTimer::HandleTimerFired(void *aContext)
{
TrickleTimer *obj = static_cast<TrickleTimer *>(aContext);
obj->HandleTimerFired();
}
void TrickleTimer::HandleTimerFired(void)
{
Phase curPhase = mPhase;
bool shouldContinue = true;
// Default the current state to Dormant
mPhase = kPhaseDormant;
switch (curPhase)
{
// We have just reached time 't'
case kPhaseTransmit:
{
// Are we not using reduncancy or is the counter still less than it?
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
if (k == 0 || c < k)
#endif
{
// Invoke the transmission callback
shouldContinue = TransmitFired();
}
// Wait for the rest of the interval to elapse
if (shouldContinue)
{
// If we are in plain timer mode, just randomize I and restart the interval
if (mMode == kModePlainTimer)
{
// Initialize I to [Imin, Imax]
I = Imin + otPlatRandomGet() % (Imax - Imin);
// Start a new interval
StartNewInterval();
}
else
{
// Start next phase of the timer
mPhase = kPhaseInterval;
// Start the time for 'I - t' milliseconds
mTimer.Start(I - t);
}
}
break;
}
// We have just reached time 'I'
case kPhaseInterval:
{
// Double 'I' to get the new interval length
uint32_t newI = I == 0 ? 1 : I << 1;
if (newI > Imax) { newI = Imax; }
I = newI;
// Invoke the interval expiration callback
shouldContinue = IntervalExpiredFired();
if (shouldContinue)
{
// Start a new interval
StartNewInterval();
}
break;
}
default:
assert(false);
}
}
} // namespace Thread
+190
View File
@@ -0,0 +1,190 @@
/*
* Copyright (c) 2016, 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.
*/
/**
* @file
* This file includes definitions for the trickle timer logic.
*/
#ifndef TRICKLE_TIMER_HPP_
#define TRICKLE_TIMER_HPP_
#include <common/timer.hpp>
namespace Thread {
/**
* @addtogroup core-timer-trickle
*
* @brief
* This module includes definitions for the trickle timer logic.
*
* @{
*
*/
/**
* This class implements a trickle timer.
*
*/
class TrickleTimer
{
public:
/**
* Represents the modes of operation for the TrickleTimer
*/
typedef enum Mode
{
kModeNormal = 0, ///< Runs the normal trickle logic.
kModePlainTimer = 1, ///< Runs a normal timer between Imin and Imax.
kModeMPL = 2, ///< Runs the trickle logic modified for MPL.
} Mode;
/**
* This function pointer is called when the timer expires.
*
* @param[in] aContext A pointer to arbitrary context information.
*
* @retval TRUE If the trickle timer should continue running.
* @retval FALSE If the trickle timer should stop running.
*/
typedef bool (*Handler)(void *aContext);
/**
* This constructor creates a trickle timer instance.
*
* @param[in] aScheduler A refrence to the timer scheduler.
* @param[in] aRedundancyConstant The redundancy constant for the timer, 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] aContext A pointer to arbitrary context information.
*
*/
TrickleTimer(TimerScheduler &aScheduler,
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
uint32_t aRedundancyConstant,
#endif
Handler aTransmitHandler, Handler aIntervalExpiredHandler, void *aContext);
/**
* This method indicates whether or not the trickle timer instance is running.
*
* @retval TRUE If the trickle timer is running.
* @retval FALSE If the trickle timer is not running.
*/
bool IsRunning(void) const;
/**
* This method start the trickle timer.
*
* @param[in] aIntervalMin The minimum interval for the timer, Imin.
* @param[in] aIntervalMax The maximum interval for the timer, Imax.
* @param[in] aMode The operating mode for the timer.
*
*/
void Start(uint32_t aIntervalMin, uint32_t aIntervalMax, Mode aMode);
/**
* This method stops the trickle timer.
*
*/
void Stop(void);
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
/**
* This method indicates to the trickle timer a 'consistent' state.
*
*/
void IndicateConsistent(void);
#endif
/**
* This method indicates to the trickle timer an 'inconsistent' state.
*
*/
void IndicateInconsistent(void);
private:
bool TransmitFired(void) { return mTransmitHandler(mContext); }
bool IntervalExpiredFired(void) { return mIntervalExpiredHandler ? mIntervalExpiredHandler(mContext) : true; }
void StartNewInterval(void);
static void HandleTimerFired(void *aContext);
void HandleTimerFired(void);
typedef enum Phase
{
///< Indicates we are currently not running
kPhaseDormant = 1,
///< Indicates that when the timer expires, it should evaluate for transmit callbacks
kPhaseTransmit = 2,
///< Indicates that when the timer expires, it should process interval expiration callbacks
kPhaseInterval = 3,
} Phase;
Timer mTimer;
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
// Redundancy constant
const uint32_t k;
#endif
// Minimum interval size
uint32_t Imin;
// Maximum interval size
uint32_t Imax;
// The mode of operation
Mode mMode;
// The current interval size (in milliseconds)
uint32_t I;
// The time (in milliseconds) into the interval at which we should transmit
uint32_t t;
#ifdef ENABLE_TRICKLE_TIMER_SUPPRESSION_SUPPORT
// A counter, keeping track of the number of "consistent" transmissions received
uint32_t c;
#endif
// The current trickle phase for the timer
Phase mPhase;
// Callback variables
Handler mTransmitHandler;
Handler mIntervalExpiredHandler;
void *mContext;
};
/**
* @}
*
*/
} // namespace Thread
#endif // TRICKLE_TIMER_HPP_
+24 -59
View File
@@ -50,7 +50,7 @@ namespace Mle {
MleRouter::MleRouter(ThreadNetif &aThreadNetif):
Mle(aThreadNetif),
mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleAdvertiseTimer, this),
mAdvertiseTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleAdvertiseTimer, NULL, this),
mStateUpdateTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleStateUpdateTimer, this),
mSocket(aThreadNetif.GetIp6().mUdp),
mAddressSolicit(OPENTHREAD_URI_ADDRESS_SOLICIT, &HandleAddressSolicit, this),
@@ -70,7 +70,6 @@ MleRouter::MleRouter(ThreadNetif &aThreadNetif):
mMaxChildrenAllowed = kMaxChildren;
mRouterId = kMaxRouterId;
mPreviousRouterId = kMaxRouterId;
mAdvertiseInterval = kAdvertiseIntervalMin;
mRouterIdSequenceLastUpdated = 0;
mRouterRoleEnabled = true;
mRouterSelectionJitterTimeout = 0;
@@ -241,7 +240,6 @@ ThreadError MleRouter::BecomeLeader(void)
mSocket.Open(&HandleUdpReceive, this);
mAdvertiseTimer.Stop();
ResetAdvertiseInterval();
mStateUpdateTimer.Start(kStateUpdatePeriod);
mAddressResolver.Clear();
@@ -267,6 +265,8 @@ ThreadError MleRouter::BecomeLeader(void)
SuccessOrExit(error = SetStateLeader(GetRloc16(mRouterId)));
ResetAdvertiseInterval();
exit:
return error;
}
@@ -299,8 +299,6 @@ ThreadError MleRouter::HandleDetachStart(void)
ThreadError MleRouter::HandleChildStart(otMleAttachFilter aFilter)
{
uint32_t advertiseDelay;
mRouterIdSequenceLastUpdated = Timer::GetNow();
StopLeader();
@@ -322,15 +320,12 @@ ThreadError MleRouter::HandleChildStart(otMleAttachFilter aFilter)
if (mDeviceMode & ModeTlv::kModeFFD)
{
advertiseDelay = Timer::SecToMsec(kReedAdvertiseInterval +
(otPlatRandomGet() % kReedAdvertiseJitter));
mAdvertiseTimer.Start(advertiseDelay);
mAdvertiseTimer.Start(
Timer::SecToMsec(kReedAdvertiseInterval),
Timer::SecToMsec(kReedAdvertiseInterval + kReedAdvertiseJitter),
TrickleTimer::kModePlainTimer);
mNetif.SubscribeAllRoutersMulticast();
}
else
{
}
return kThreadError_None;
}
@@ -414,68 +409,38 @@ void MleRouter::SetRouterDowngradeThreshold(uint8_t aThreshold)
mRouterDowngradeThreshold = aThreshold;
}
void MleRouter::HandleAdvertiseTimer(void *aContext)
bool MleRouter::HandleAdvertiseTimer(void *aContext)
{
MleRouter *obj = reinterpret_cast<MleRouter *>(aContext);
obj->HandleAdvertiseTimer();
MleRouter *obj = static_cast<MleRouter *>(aContext);
return obj->HandleAdvertiseTimer();
}
void MleRouter::HandleAdvertiseTimer(void)
bool MleRouter::HandleAdvertiseTimer(void)
{
uint32_t advertiseDelay;
if ((mDeviceMode & ModeTlv::kModeFFD) == 0)
{
return;
return false;
}
SendAdvertisement();
switch (GetDeviceState())
{
case kDeviceStateDisabled:
case kDeviceStateDetached:
assert(false);
break;
case kDeviceStateChild:
advertiseDelay = Timer::SecToMsec(kReedAdvertiseInterval +
(otPlatRandomGet() % kReedAdvertiseJitter));
mAdvertiseTimer.Start(advertiseDelay);
break;
case kDeviceStateRouter:
case kDeviceStateLeader:
mAdvertiseInterval *= 2;
if (mAdvertiseInterval > kAdvertiseIntervalMax)
{
mAdvertiseInterval = kAdvertiseIntervalMax;
}
advertiseDelay = Timer::SecToMsec(mAdvertiseInterval) / 2;
advertiseDelay += otPlatRandomGet() % (advertiseDelay);
mAdvertiseTimer.Start(advertiseDelay);
break;
}
return true;
}
ThreadError MleRouter::ResetAdvertiseInterval(void)
void MleRouter::ResetAdvertiseInterval(void)
{
uint32_t advertiseDelay;
assert(GetDeviceState() == kDeviceStateRouter ||
GetDeviceState() == kDeviceStateLeader);
VerifyOrExit(mAdvertiseInterval != kAdvertiseIntervalMin || !mAdvertiseTimer.IsRunning(), ;);
if (!mAdvertiseTimer.IsRunning())
{
mAdvertiseTimer.Start(
Timer::SecToMsec(kAdvertiseIntervalMin),
Timer::SecToMsec(kAdvertiseIntervalMax),
TrickleTimer::kModeNormal);
}
mAdvertiseInterval = kAdvertiseIntervalMin;
advertiseDelay = Timer::SecToMsec(mAdvertiseInterval) / 2;
advertiseDelay += otPlatRandomGet() % advertiseDelay;
mAdvertiseTimer.Start(advertiseDelay);
otLogInfoMle("reset advertise interval\n");
exit:
return kThreadError_None;
mAdvertiseTimer.IndicateInconsistent();
}
ThreadError MleRouter::SendAdvertisement(void)
+5 -5
View File
@@ -37,6 +37,7 @@
#include <coap/coap_header.hpp>
#include <coap/coap_server.hpp>
#include <common/timer.hpp>
#include <common/trickle_timer.hpp>
#include <mac/mac_frame.hpp>
#include <net/icmp6.hpp>
#include <net/udp6.hpp>
@@ -500,7 +501,7 @@ private:
ThreadError HandleNetworkDataUpdateRouter(void);
ThreadError ProcessRouteTlv(const RouteTlv &aRoute);
ThreadError ResetAdvertiseInterval(void);
void ResetAdvertiseInterval(void);
ThreadError SendAddressSolicit(ThreadStatusTlv::Status aStatus);
ThreadError SendAddressRelease(void);
void SendAddressSolicitResponse(const Coap::Header &aRequest, uint8_t aRouterId,
@@ -547,12 +548,12 @@ private:
uint8_t AllocateRouterId(uint8_t aRouterId);
bool InRouterIdMask(uint8_t aRouterId);
static void HandleAdvertiseTimer(void *aContext);
void HandleAdvertiseTimer(void);
static bool HandleAdvertiseTimer(void *aContext);
bool HandleAdvertiseTimer(void);
static void HandleStateUpdateTimer(void *aContext);
void HandleStateUpdateTimer(void);
Timer mAdvertiseTimer;
TrickleTimer mAdvertiseTimer;
Timer mStateUpdateTimer;
Ip6::UdpSocket mSocket;
@@ -577,7 +578,6 @@ private:
uint8_t mRouterId;
uint8_t mPreviousRouterId;
uint32_t mAdvertiseInterval;
Coap::Server &mCoapServer;
uint8_t mCoapToken[2];