From f067d68f3864b8222eb1e0a483bbcf931d63fe86 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 16 Jun 2017 09:28:52 -0700 Subject: [PATCH] Timer: Modify timer implementation to store fire time (#1904) This commit changes the `Timer` class to store the fire time instead of start time and duration. This simplifies the `Timer` implementation and reduces the size of a `Timer` instance. The `test_timer` unit test is also updated to add new test cases to verify correct timer behavior during 32-bit timer wrap. This commit also simplifies the key rotation implementation in `KeyManager` class. Since the key rotation time and guard time are provided in hours unit and can span multiple days, the code tracks number of hours since last key rotation in `mHoursSinceKeyRotation` (using a one hour interval timer). This is then used to decide when/if to change the key sequence. --- src/core/coap/coap.cpp | 2 +- src/core/common/timer.cpp | 57 +++---------- src/core/common/timer.hpp | 71 ++++++++-------- src/core/net/dns_client.cpp | 2 +- src/core/net/ip6_mpl.cpp | 2 +- src/core/thread/data_poll_manager.cpp | 6 +- src/core/thread/data_poll_manager.hpp | 1 + src/core/thread/key_manager.cpp | 48 +++++------ src/core/thread/key_manager.hpp | 6 +- src/core/thread/mle.cpp | 2 +- tests/unit/test_timer.cpp | 112 ++++++++++++++++++-------- 11 files changed, 170 insertions(+), 139 deletions(-) diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index fe202642c..6842d3dd8 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -405,7 +405,7 @@ Message *Coap::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLeng if (mRetransmissionTimer.IsRunning()) { // If timer is already running, check if it should be restarted with earlier fire time. - alarmFireTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + alarmFireTime = mRetransmissionTimer.GetFireTime(); if (aCoapMetadata.IsEarlier(alarmFireTime)) { diff --git a/src/core/common/timer.cpp b/src/core/common/timer.cpp index 17b70dfc7..2e927d5cd 100644 --- a/src/core/common/timer.cpp +++ b/src/core/common/timer.cpp @@ -69,7 +69,7 @@ void TimerScheduler::Add(Timer &aTimer) for (cur = mHead; cur; cur = cur->mNext) { - if (TimerCompare(aTimer, *cur)) + if (IsStrictlyBefore(aTimer.mFireTime, cur->mFireTime)) { if (prev) { @@ -126,18 +126,14 @@ exit: void TimerScheduler::SetAlarm(void) { - uint32_t now = otPlatAlarmGetNow(); - uint32_t elapsed; - uint32_t remaining; - if (mHead == NULL) { otPlatAlarmStop(GetIp6()->GetInstance()); } else { - elapsed = now - mHead->mT0; - remaining = (mHead->mDt > elapsed) ? mHead->mDt - elapsed : 0; + uint32_t now = otPlatAlarmGetNow(); + uint32_t remaining = IsStrictlyBefore(now, mHead->mFireTime) ? (mHead->mFireTime - now) : 0; otPlatAlarmStartAt(GetIp6()->GetInstance(), now, remaining); } @@ -146,21 +142,17 @@ void TimerScheduler::SetAlarm(void) extern "C" void otPlatAlarmFired(otInstance *aInstance) { otLogFuncEntry(); - aInstance->mIp6.mTimerScheduler.FireTimers(); + aInstance->mIp6.mTimerScheduler.ProcessTimers(); otLogFuncExit(); } -void TimerScheduler::FireTimers(void) +void TimerScheduler::ProcessTimers(void) { - uint32_t now = otPlatAlarmGetNow(); - uint32_t elapsed; Timer *timer = mHead; if (timer) { - elapsed = now - timer->mT0; - - if (elapsed >= timer->mDt) + if (!IsStrictlyBefore(otPlatAlarmGetNow(), timer->mFireTime)) { Remove(*timer); timer->Fired(); @@ -181,39 +173,16 @@ Ip6::Ip6 *TimerScheduler::GetIp6(void) return Ip6::Ip6FromTimerScheduler(this); } -bool TimerScheduler::TimerCompare(const Timer &aTimerA, const Timer &aTimerB) +bool TimerScheduler::IsStrictlyBefore(uint32_t aTimeA, uint32_t aTimeB) { - uint32_t now = otPlatAlarmGetNow(); - uint32_t elapsedA = now - aTimerA.mT0; - uint32_t elapsedB = now - aTimerB.mT0; - bool retval = false; + uint32_t diff = aTimeA - aTimeB; - if (aTimerA.mDt >= elapsedA && aTimerB.mDt >= elapsedB) - { - uint32_t remainingA = aTimerA.mDt - elapsedA; - uint32_t remainingB = aTimerB.mDt - elapsedB; + // Three cases: + // 1) aTimeA is before aTimeB => Difference is negative (last bit of difference is set) => Returning true. + // 2) aTimeA is same as aTimeB => Difference is zero (last bit of difference is clear) => Returning false. + // 3) aTimeA is after aTimeB => Difference is positive (last bit of difference is clear) => Returning false. - if (remainingA < remainingB) - { - retval = true; - } - } - else if (aTimerA.mDt < elapsedA && aTimerB.mDt >= elapsedB) - { - retval = true; - } - else if (aTimerA.mDt < elapsedA && aTimerB.mDt < elapsedB) - { - uint32_t expiredByA = elapsedA - aTimerA.mDt; - uint32_t expiredByB = elapsedB - aTimerB.mDt; - - if (expiredByB < expiredByA) - { - retval = true; - } - } - - return retval; + return ((diff & (1UL << 31)) != 0); } } // namespace ot diff --git a/src/core/common/timer.hpp b/src/core/common/timer.hpp index ba5886d3f..e1e1f6dbf 100644 --- a/src/core/common/timer.hpp +++ b/src/core/common/timer.hpp @@ -40,6 +40,7 @@ #include #include +#include "common/debug.hpp" #include "common/tasklet.hpp" namespace ot { @@ -90,12 +91,17 @@ public: void Remove(Timer &aTimer); /** - * This method processes all running timers. - * - * @param[in] aContext A pointer to arbitrary context information. + * This method processes the running timers. * */ - void FireTimers(void); + void ProcessTimers(void); + +private: + /** + * This method sets the platform alarm based on timer at front of the list. + * + */ + void SetAlarm(void); /** * This method returns the pointer to the parent Ip6 structure. @@ -105,20 +111,20 @@ public: */ Ip6::Ip6 *GetIp6(void); -private: - void SetAlarm(void); - /** - * This method compares two timers and returns a value to indicate - * which timer will fire earlier. + * This static method compares two times and indicates if the first time is strictly before (earlier) than the + * second time. * - * @param[in] aTimerA The first timer for comparison. - * @param[in] aTimerB The second timer for comparison. + * This method requires that the difference between the two given times to be smaller than kMaxDt. + * + * @param[in] aTimerA The first time for comparison. + * @param[in] aTimerB The second time for comparison. + * + * @returns TRUE if aTimeA is before aTimeB. + * @returns FALSE if aTimeA is same time or after aTimeB. * - * @returns true if aTimerA will fire before aTimerB. - * @returns false if aTimerA will fire at the same time or after aTimerB. */ - static bool TimerCompare(const Timer &aTimerA, const Timer &aTimerB); + static bool IsStrictlyBefore(uint32_t aTimeA, uint32_t aTimeB); Timer *mHead; }; @@ -132,6 +138,12 @@ class Timer friend class TimerScheduler; public: + + enum + { + kMaxDt = (1UL << 31) - 1, //< Maximum permitted value for parameter `aDt` in `Start` and `StartAt` method. + }; + /** * This function pointer is called when the timer expires. * @@ -151,32 +163,24 @@ public: mScheduler(aScheduler), mHandler(aHandler), mContext(aContext), - mT0(0), - mDt(0), + mFireTime(0), mNext(this) { } /** - * This method returns the start time in milliseconds for the timer. + * This method returns the fire time of the timer. * - * @returns The start time in milliseconds. + * @returns The fire time in milliseconds. * */ - uint32_t Gett0(void) const { return mT0; } - - /** - * This method returns the delta time in milliseconds for the timer. - * - * @returns The delta time. - * - */ - uint32_t Getdt(void) const { return mDt; } + uint32_t GetFireTime(void) const { return mFireTime; } /** * This method indicates whether or not the timer instance is running. * * @retval TRUE If the timer is running. * @retval FALSE If the timer is not running. + * */ bool IsRunning(void) const { return (mNext != this); } @@ -184,16 +188,20 @@ public: * This method schedules the timer to fire a @p dt milliseconds from now. * * @param[in] aDt The expire time in milliseconds from now. + * (aDt must be smaller than or equal to kMaxDt). + * */ void Start(uint32_t aDt) { StartAt(GetNow(), aDt); } /** - * This method schedules the timer to fire at @p dt milliseconds from @p t0. + * This method schedules the timer to fire at @p aDt milliseconds from @p aT0. * * @param[in] aT0 The start time in milliseconds. - * @param[in] aDt The expire time in milliseconds from @p t0. + * @param[in] aDt The expire time in milliseconds from @p aT0. + * (aDt must be smaller than or equal to kMaxDt). + * */ - void StartAt(uint32_t aT0, uint32_t aDt) { mT0 = aT0; mDt = aDt; mScheduler.Add(*this); } + void StartAt(uint32_t aT0, uint32_t aDt) { assert(aDt <= kMaxDt); mFireTime = aT0 + aDt; mScheduler.Add(*this); } /** * This method stops the timer. @@ -247,8 +255,7 @@ private: TimerScheduler &mScheduler; Handler mHandler; void *mContext; - uint32_t mT0; - uint32_t mDt; + uint32_t mFireTime; Timer *mNext; }; diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index 0ddb93bc6..b0333f95d 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -169,7 +169,7 @@ Message *Client::CopyAndEnqueueMessage(const Message &aMessage, const QueryMetad if (mRetransmissionTimer.IsRunning()) { // If timer is already running, check if it should be restarted with earlier fire time. - nextTransmissionTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + nextTransmissionTime = mRetransmissionTimer.GetFireTime(); if (aQueryMetadata.IsEarlier(nextTransmissionTime)) { diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 3cb3dc213..4393f66b8 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -192,7 +192,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque if (mRetransmissionTimer.IsRunning()) { // If timer is already running, check if it should be restarted with earlier fire time. - nextTransmissionTime = mRetransmissionTimer.Gett0() + mRetransmissionTimer.Getdt(); + nextTransmissionTime = mRetransmissionTimer.GetFireTime(); if (messageMetadata.IsEarlier(nextTransmissionTime)) { diff --git a/src/core/thread/data_poll_manager.cpp b/src/core/thread/data_poll_manager.cpp index a3d2df830..6aa11adcc 100644 --- a/src/core/thread/data_poll_manager.cpp +++ b/src/core/thread/data_poll_manager.cpp @@ -51,6 +51,7 @@ namespace ot { DataPollManager::DataPollManager(MeshForwarder &aMeshForwarder): mMeshForwarder(aMeshForwarder), mTimer(aMeshForwarder.GetNetif().GetIp6().mTimerScheduler, &DataPollManager::HandlePollTimer, this), + mTimerStartTime(0), mExternalPollPeriod(0), mPollPeriod(0), mEnabled(false), @@ -327,11 +328,12 @@ void DataPollManager::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector) if (mTimer.IsRunning()) { - mTimer.StartAt(mTimer.Gett0(), mPollPeriod); + mTimer.StartAt(mTimerStartTime, mPollPeriod); } else { - mTimer.Start(mPollPeriod); + mTimerStartTime = Timer::GetNow(); + mTimer.StartAt(mTimerStartTime, mPollPeriod); } } diff --git a/src/core/thread/data_poll_manager.hpp b/src/core/thread/data_poll_manager.hpp index 38ae16eab..bbf28d808 100644 --- a/src/core/thread/data_poll_manager.hpp +++ b/src/core/thread/data_poll_manager.hpp @@ -222,6 +222,7 @@ private: MeshForwarder &mMeshForwarder; Timer mTimer; + uint32_t mTimerStartTime; uint32_t mExternalPollPeriod; uint32_t mPollPeriod; diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index 2f82bdb29..4d2df0afc 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -56,6 +56,7 @@ KeyManager::KeyManager(ThreadNetif &aThreadNetif): mMleFrameCounter(0), mStoredMacFrameCounter(0), mStoredMleFrameCounter(0), + mHoursSinceKeyRotation(0), mKeyRotationTime(kDefaultKeyRotationTime), mKeySwitchGuardTime(kDefaultKeySwitchGuardTime), mKeySwitchGuardEnabled(false), @@ -68,7 +69,7 @@ KeyManager::KeyManager(ThreadNetif &aThreadNetif): void KeyManager::Start(void) { mKeySwitchGuardEnabled = false; - mKeyRotationTimer.Start(Timer::HoursToMsec(mKeyRotationTime)); + StartKeyRotationTimer(); } void KeyManager::Stop(void) @@ -170,25 +171,7 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) mKeyRotationTimer.IsRunning() && mKeySwitchGuardEnabled) { - uint32_t now = Timer::GetNow(); - uint32_t guardStartTimestamp = mKeyRotationTimer.Gett0(); - uint32_t guardEndTimestamp = guardStartTimestamp + Timer::HoursToMsec(mKeySwitchGuardTime); - - // Check for timer overflow - if (guardEndTimestamp < mKeyRotationTimer.Gett0()) - { - if ((now > guardStartTimestamp) || (now < guardEndTimestamp)) - { - ExitNow(); - } - } - else - { - if ((now > guardStartTimestamp) && (now < guardEndTimestamp)) - { - ExitNow(); - } - } + VerifyOrExit(mHoursSinceKeyRotation < mKeySwitchGuardTime); } mKeySequence = aKeySequence; @@ -200,7 +183,7 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) if (mKeyRotationTimer.IsRunning()) { mKeySwitchGuardEnabled = true; - mKeyRotationTimer.Start(Timer::HoursToMsec(mKeyRotationTime)); + StartKeyRotationTimer(); } mNetif.SetStateChangedFlags(OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER); @@ -252,7 +235,6 @@ otError KeyManager::SetKeyRotation(uint32_t aKeyRotation) otError result = OT_ERROR_NONE; VerifyOrExit(aKeyRotation >= static_cast(kMinKeyRotationTime), result = OT_ERROR_INVALID_ARGS); - VerifyOrExit(aKeyRotation <= static_cast(kMaxKeyRotationTime), result = OT_ERROR_INVALID_ARGS); mKeyRotationTime = aKeyRotation; @@ -260,6 +242,12 @@ exit: return result; } +void KeyManager::StartKeyRotationTimer(void) +{ + mHoursSinceKeyRotation = 0; + mKeyRotationTimer.Start(kOneHourIntervalInMsec); +} + void KeyManager::HandleKeyRotationTimer(void *aContext) { static_cast(aContext)->HandleKeyRotationTimer(); @@ -267,7 +255,21 @@ void KeyManager::HandleKeyRotationTimer(void *aContext) void KeyManager::HandleKeyRotationTimer(void) { - SetCurrentKeySequence(mKeySequence + 1); + mHoursSinceKeyRotation++; + + // Order of operations below is important. We should restart the timer (from + // last fire time for one hour interval) before potentially calling + // `SetCurrentKeySequence()`. `SetCurrentKeySequence()` uses the fact that + // timer is running to decide to check for the guard time and to reset the + // rotation timer (and the `mHoursSinceKeyRotation`) if it updates the key + // sequence. + + mKeyRotationTimer.StartAt(mKeyRotationTimer.GetFireTime(), kOneHourIntervalInMsec); + + if (mHoursSinceKeyRotation >= mKeyRotationTime) + { + SetCurrentKeySequence(mKeySequence + 1); + } } } // namespace ot diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index 9a0db76d9..3cec3c210 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -272,7 +272,7 @@ public: * This method sets the KeyRotation time. * * The KeyRotation time is the time interval after witch security key will be automatically rotated. - * It's value shall be in range [kMinKeyRotationTime, kMaxKeyRotationTime]. + * Its value shall be larger than or equal to kMinKeyRotationTime. * * @param[in] aKeyRotation The KeyRotation value in hours. * @@ -328,14 +328,15 @@ private: enum { kMinKeyRotationTime = 1, - kMaxKeyRotationTime = 0xffffffff / 3600u / 1000u, kDefaultKeyRotationTime = 672, kDefaultKeySwitchGuardTime = 624, kMacKeyOffset = 16, + kOneHourIntervalInMsec = 3600u * 1000u, }; otError ComputeKey(uint32_t aKeySequence, uint8_t *aKey); + void StartKeyRotationTimer(void); static void HandleKeyRotationTimer(void *aContext); void HandleKeyRotationTimer(void); @@ -353,6 +354,7 @@ private: uint32_t mStoredMacFrameCounter; uint32_t mStoredMleFrameCounter; + uint32_t mHoursSinceKeyRotation; uint32_t mKeyRotationTime; uint32_t mKeySwitchGuardTime; bool mKeySwitchGuardEnabled; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 77ef4fb59..85a551381 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1975,7 +1975,7 @@ otError Mle::AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestinat if (mDelayedResponseTimer.IsRunning()) { // If timer is already running, check if it should be restarted with earlier fire time. - alarmFireTime = mDelayedResponseTimer.Gett0() + mDelayedResponseTimer.Getdt(); + alarmFireTime = mDelayedResponseTimer.GetFireTime(); if (delayedResponse.IsEarlier(alarmFireTime)) { diff --git a/tests/unit/test_timer.cpp b/tests/unit/test_timer.cpp index b917f5e02..f31114b3f 100644 --- a/tests/unit/test_timer.cpp +++ b/tests/unit/test_timer.cpp @@ -103,6 +103,8 @@ int TestOneTimer(void) InitTestTimer(); InitCounters(); + printf("TestOneTimer() "); + sNow = kTimeT0; timer.Start(kTimerInterval); @@ -130,12 +132,12 @@ int TestOneTimer(void) sNow = 0 - (kTimerInterval - 2); timer.Start(kTimerInterval); - VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "TestOneTimer: Start CallCount Failed.\n"); - VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "TestOneTimer: Stop CallCount Failed.\n"); - VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "TestOneTimer: Handler CallCount Failed.\n"); - VerifyOrQuit(sPlatT0 == 0 - (kTimerInterval - 2) && sPlatDt == 10, "TestOneTimer: Start params Failed.\n"); - VerifyOrQuit(timer.IsRunning(), "TestOneTimer: Timer running Failed.\n"); - VerifyOrQuit(sTimerOn, "TestOneTimer: Platform Timer State Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "TestOneTimer: Start CallCount Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "TestOneTimer: Stop CallCount Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "TestOneTimer: Handler CallCount Failed.\n"); + VerifyOrQuit(sPlatT0 == 0 - (kTimerInterval - 2) && sPlatDt == 10, "TestOneTimer: Start params Failed.\n"); + VerifyOrQuit(timer.IsRunning(), "TestOneTimer: Timer running Failed.\n"); + VerifyOrQuit(sTimerOn, "TestOneTimer: Platform Timer State Failed.\n"); sNow += kTimerInterval; @@ -205,13 +207,19 @@ int TestOneTimer(void) VerifyOrQuit(timer.IsRunning() == false, "TestOneTimer: Timer running Failed.\n"); VerifyOrQuit(sTimerOn == false, "TestOneTimer: Platform Timer State Failed.\n"); + printf(" --> PASSED\n"); + return 0; } + /** * Test the TimerScheduler's behavior of ten timers started and fired. + * + * `aTimeShift` is added to the t0 and trigger times for all timers. It can be used to check the ten timer behavior + * at different start time (e.g., around a 32-bit wrap). */ -int TestTenTimers(void) +static void TenTimers(uint32_t aTimeShift) { const uint32_t kNumTimers = 10; const uint32_t kNumTriggers = 7; @@ -232,27 +240,27 @@ int TestTenTimers(void) { 20, 100, - (0 - kTimeT0[2]), + (ot::Timer::kMaxDt - kTimeT0[2]), 100000, 1000000, 10, - (1000 - kTimeT0[6]), + ot::Timer::kMaxDt, 200, 200, 200 }; // Expected timer fire order - // timer # Trigger time - // 5 1014 - // 0 1020 - // 1 1100 - // 7 1206 - // 8 1207 - // 9 1208 - // 3 101002 - // 4 1001003 - // 2 0 - // 6 1000 + // timer # Trigger time + // 5 1014 + // 0 1020 + // 1 1100 + // 7 1206 + // 8 1207 + // 9 1208 + // 3 101002 + // 4 1001003 + // 2 kMaxDt + // 6 kMaxDt + 1005 const uint32_t kTriggerTimes[kNumTriggers] = { 1014, @@ -260,9 +268,8 @@ int TestTenTimers(void) 1100, 1207, 101004, - /* timer wrap here */ - 2, - 1000 + ot::Timer::kMaxDt, + ot::Timer::kMaxDt + kTimeT0[6] }; // Expected timers fired by each kTriggerTimes[] value // Trigger # Timers Fired @@ -341,9 +348,23 @@ int TestTenTimers(void) ot::Timer timer7(aInstance.mIp6.mTimerScheduler, TestTimerHandler, &timerContextHandleCounter[7]); ot::Timer timer8(aInstance.mIp6.mTimerScheduler, TestTimerHandler, &timerContextHandleCounter[8]); ot::Timer timer9(aInstance.mIp6.mTimerScheduler, TestTimerHandler, &timerContextHandleCounter[9]); - ot::Timer *timers[kNumTimers] = {&timer0, &timer1, &timer2, &timer3, &timer4, &timer5, &timer6, &timer7, &timer8, &timer9}; + ot::Timer *timers[kNumTimers] = + { + &timer0, + &timer1, + &timer2, + &timer3, + &timer4, + &timer5, + &timer6, + &timer7, + &timer8, + &timer9 + }; size_t i; + printf("TestTenTimer() with aTimeShift=%-10u ", aTimeShift); + // Start the Ten timers. InitTestTimer(); @@ -351,17 +372,18 @@ int TestTenTimers(void) for (i = 0; i < kNumTimers ; i++) { - sNow = kTimeT0[i]; + sNow = kTimeT0[i] + aTimeShift; timers[i]->Start(kTimerInterval[i]); } // given the order in which timers are started, the TimerScheduler should call otPlatAlarmStartAt 2 times. // one for timer[0] and one for timer[5] which will supercede timer[0]. - VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "TestTenTimer: Start CallCount Failed.\n"); - VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "TestTenTimer: Stop CallCount Failed.\n"); - VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "TestTenTimer: Handler CallCount Failed.\n"); - VerifyOrQuit(sPlatT0 == kTimeT0[5] && sPlatDt == kTimerInterval[5], "TestTenTimer: Start params Failed.\n"); - VerifyOrQuit(sTimerOn, "TestTenTimer: Platform Timer State Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "TestTenTimer: Start CallCount Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "TestTenTimer: Stop CallCount Failed.\n"); + VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "TestTenTimer: Handler CallCount Failed.\n"); + VerifyOrQuit(sPlatT0 == kTimeT0[5] + aTimeShift, "TestTenTimer: Start params Failed.\n"); + VerifyOrQuit(sPlatDt == kTimerInterval[5], "TestTenTimer: Start params Failed.\n"); + VerifyOrQuit(sTimerOn, "TestTenTimer: Platform Timer State Failed.\n"); for (i = 0 ; i < kNumTimers ; i++) { @@ -372,7 +394,7 @@ int TestTenTimers(void) for (size_t trigger = 0 ; trigger < kNumTriggers ; trigger++) { - sNow = kTriggerTimes[trigger]; + sNow = kTriggerTimes[trigger] + aTimeShift; do { @@ -397,7 +419,10 @@ int TestTenTimers(void) for (i = 0 ; i < kNumTimers ; i++) { - VerifyOrQuit(timers[i]->IsRunning() == kTimerStateAfterTrigger[trigger][i], "TestTenTimer: Timer running Failed.\n"); + VerifyOrQuit( + timers[i]->IsRunning() == kTimerStateAfterTrigger[trigger][i], + "TestTenTimer: Timer running Failed.\n" + ); } } @@ -406,6 +431,29 @@ int TestTenTimers(void) VerifyOrQuit(timerContextHandleCounter[i] == 1, "TestTenTimer: Timer context counter Failed.\n"); } + printf("--> PASSED\n"); +} + +int TestTenTimers(void) +{ + // Time shift to change the start/fire time of ten timers. + const uint32_t kTimeShift[] = + { + 0, + 100000U, + 0U - 1U, + 0U - 1100U, + ot::Timer::kMaxDt, + ot::Timer::kMaxDt + 1020U, + }; + + size_t i; + + for (i = 0; i < sizeof(kTimeShift) / sizeof(kTimeShift[0]); i++) + { + TenTimers(kTimeShift[i]); + } + return 0; }