blob: add5d49bcdc6a939ea1dac89346c1e8b1475eeb7 [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines the member functions and private data for
* the nl::Weave::System::Timer class, which is used for
* representing an in-progress one-shot timer.
*/
// Include module header
#include <SystemLayer/SystemTimer.h>
// Include common private header
#include "SystemLayerPrivate.h"
// Include local headers
#include <string.h>
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <lwip/sys.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if !(HAVE_CLOCK_GETTIME) && HAVE_GETTIMEOFDAY
#include <errno.h>
#include <sys/time.h>
#if !(HAVE_CLOCKID_T)
typedef int clockid_t;
#endif
extern "C" int clock_gettime(clockid_t clk_id, struct timespec* t);
#endif // !(HAVE_CLOCK_GETTIME) && HAVE_GETTIMEOFDAY
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <SystemLayer/SystemError.h>
#include <SystemLayer/SystemLayer.h>
#include <SystemLayer/SystemFaultInjection.h>
#include <Weave/Support/CodeUtils.h>
/*******************************************************************************
* Timer state
*
* There are two fundamental state-change variables: Object::mSystemLayer and
* Timer::OnComplete. These must be checked and changed atomically. The state
* of the timer is governed by the following state machine:
*
* INITIAL STATE: mSystemLayer == NULL, OnComplete == NULL
* |
* V
* UNALLOCATED<-----------------------------+
* | |
* (set mSystemLayer != NULL) |
* | |
* V |
* ALLOCATED-------(set mSystemLayer NULL)--+
* | \-----------------------------+
* | |
* (set OnComplete != NULL) |
* | |
* V |
* ARMED ---------( clear OnComplete )--+
*
* When in the ARMED state:
*
* * None of the member variables may mutate.
* * OnComplete must only be cleared by Cancel() or HandleComplete()
* * Cancel() and HandleComplete() will test that they are the one to
* successfully set OnComplete NULL. And if so, that will be the
* thread that must call Object::Release().
*
*******************************************************************************
*/
namespace nl {
namespace Weave {
namespace System {
ObjectPool<Timer, WEAVE_SYSTEM_CONFIG_NUM_TIMERS> Timer::sPool;
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if HAVE_DECL_CLOCK_BOOTTIME
// CLOCK_BOOTTIME is a Linux-specific option to clock_gettime for a clock which compensates for system sleep
#define NL_SYSTEM_TIMER_CLOCK_ID CLOCK_BOOTTIME
#elif HAVE_DECL_CLOCK_MONOTONIC
// CLOCK_MONOTONIC is defined in POSIX and hence is the default choice
#define NL_SYSTEM_TIMER_CLOCK_ID CLOCK_MONOTONIC
#else
// in case there is no POSIX-compliant clock_gettime, we're most likely going to use the emulation
// implementation provided in this file, which only provides emulation for 1 clock
#define NL_SYSTEM_TIMER_CLOCK_ID 0
#endif // HAVE_DECL_CLOCK_BOOTTIME
#if !(HAVE_CLOCK_GETTIME) && HAVE_GETTIMEOFDAY
/**
* This implements a version of the of the POSIX clock_gettime method based on gettimeofday
*
* @param[in] clk_id The identifier of the particular clock on which to get the time.
* @param[out] t A timespec structure that will be filled in on success.
*
* @retval 0 on success; otherwise, -1 on failure (in which case errno is set appropriately).
*
*/
extern "C" int clock_gettime(clockid_t clk_id, struct timespec* t)
{
struct timeval now;
int retval = 0;
if (clk_id != NL_SYSTEM_TIMER_CLOCK_ID)
{
errno = EINVAL;
retval = -1;
}
else
{
retval = gettimeofday(&now, NULL);
if (retval == 0)
{
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
}
}
return retval;
}
#endif // !(HAVE_CLOCK_GETTIME) && HAVE_GETTIMEOFDAY
/**
* This method returns the current epoch, corrected by system sleep with the system timescale, in milliseconds.
*
* @return A timestamp in milliseconds.
*/
Timer::Epoch Timer::GetCurrentEpoch()
{
struct timespec tv;
clock_gettime(NL_SYSTEM_TIMER_CLOCK_ID, &tv);
return (static_cast<Timer::Epoch>(tv.tv_sec) * kTimerFactor_milli_per_unit) +
(static_cast<Timer::Epoch>(tv.tv_nsec) / kTimerFactor_nano_per_milli);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
Timer::Epoch Timer::GetCurrentEpoch()
{
static volatile Timer::Epoch overflow = 0;
static volatile u32_t lastSample = 0;
static volatile uint8_t lock = 0;
static const Timer::Epoch kOverflowIncrement = static_cast<Timer::Epoch>(0x100000000);
Timer::Epoch overflowSample;
u32_t sample;
// Tracking timer wrap assumes that this function gets called with
// a period that is less than 1/2 the timer range.
if (__sync_bool_compare_and_swap(&lock, 0, 1))
{
sample = sys_now();
if (lastSample > sample)
{
overflow += kOverflowIncrement;
}
lastSample = sample;
overflowSample = overflow;
__sync_bool_compare_and_swap(&lock, 1, 0);
}
else
{
// a lower priority task is in the block above. Depending where that
// lower task is blocked can spell trouble in a timer wrap condition.
// the question here is what this task should use as an overflow value.
// To fix this race requires a platform api that can be used to
// protect critical sections.
overflowSample = overflow;
sample = sys_now();
}
return static_cast<Timer::Epoch>(overflowSample | static_cast<Timer::Epoch>(sample));
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/**
* Compares two Timer::Epoch values and returns true if the first value is earlier than the second value.
*
* @brief
* A static API that gets called to compare 2 time values. This API attempts to account for timer wrap by assuming that the
* difference between the 2 input values will only be more than half the Epoch scalar range if a timer wrap has occurred
* between the 2 samples.
*
* @note
* This implementation assumes that Timer::Epoch is an unsigned scalar type.
*
* @return true if the first param is earlier than the second, false otherwise.
*/
bool Timer::IsEarlierEpoch(const Timer::Epoch &inFirst, const Timer::Epoch &inSecond)
{
static const Epoch kMaxTime_2 = static_cast<Epoch>((static_cast<Epoch>(0) - static_cast<Epoch>(1)) / 2);
// account for timer wrap with the assumption that no two input times will "naturally"
// be more than half the timer range apart.
return (((inFirst < inSecond) && (inSecond - inFirst < kMaxTime_2)) ||
((inFirst > inSecond) && (inFirst - inSecond > kMaxTime_2)));
}
/**
* This method registers an one-shot timer with the underlying timer mechanism provided by the platform.
*
* @param[in] aDelayMilliseconds The number of milliseconds before this timer fires
* @param[in] onComplete A pointer to the callback function when this timer fires
* @param[in] appState An arbitrary pointer to be passed into onComplete when this timer fires
*
* @retval #WEAVE_SYSTEM_NO_ERROR Unconditionally.
*
*/
Error Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, void* aAppState)
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
Layer& lLayer = this->SystemLayer();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
WEAVE_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, aDelayMilliseconds = 0);
this->AppState = aAppState;
this->mAwakenEpoch = Timer::GetCurrentEpoch() + static_cast<Epoch>(aDelayMilliseconds);
if (!__sync_bool_compare_and_swap(&this->OnComplete, NULL, aOnComplete))
{
WeaveDie();
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
// add to the sorted list of timers. Earliest timer appears first.
if (lLayer.mTimerList == NULL ||
this->IsEarlierEpoch(this->mAwakenEpoch, lLayer.mTimerList->mAwakenEpoch))
{
this->mNextTimer = lLayer.mTimerList;
lLayer.mTimerList = this;
// this is the new eariest timer and so the timer needs (re-)starting provided that
// the system is not currently processing expired timers, in which case it is left to
// HandleExpiredTimers() to re-start the timer.
if (!lLayer.mTimerComplete)
{
lLayer.StartPlatformTimer(aDelayMilliseconds);
}
}
else
{
Timer* lTimer = lLayer.mTimerList;
while (lTimer->mNextTimer)
{
if (this->IsEarlierEpoch(this->mAwakenEpoch, lTimer->mNextTimer->mAwakenEpoch))
{
// found the insert location.
break;
}
lTimer = lTimer->mNextTimer;
}
this->mNextTimer = lTimer->mNextTimer;
lTimer->mNextTimer = this;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
return WEAVE_SYSTEM_NO_ERROR;
}
Error Timer::ScheduleWork(OnCompleteFunct aOnComplete, void* aAppState)
{
Error err = WEAVE_SYSTEM_NO_ERROR;
Layer& lLayer = this->SystemLayer();
this->AppState = aAppState;
this->mAwakenEpoch = Timer::GetCurrentEpoch();
if (!__sync_bool_compare_and_swap(&this->OnComplete, NULL, aOnComplete))
{
WeaveDie();
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = lLayer.PostEvent(*this, Weave::System::kEvent_ScheduleWork, 0);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
lLayer.WakeSelect();
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
return err;
}
/**
* This method de-initializes the timer object, and prevents this timer from firing if it hasn't done so.
*
* @retval #WEAVE_SYSTEM_NO_ERROR Unconditionally.
*/
Error Timer::Cancel()
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
Layer& lLayer = this->SystemLayer();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
OnCompleteFunct lOnComplete = this->OnComplete;
// Check if the timer is armed
VerifyOrExit(lOnComplete != NULL, );
// Atomically disarm if the value has not changed
VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, NULL), );
// Since this thread changed the state of OnComplete, release the timer.
this->AppState = NULL;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
if (lLayer.mTimerList)
{
if (this == lLayer.mTimerList)
{
lLayer.mTimerList = this->mNextTimer;
}
else
{
Timer* lTimer = lLayer.mTimerList;
while (lTimer->mNextTimer)
{
if (this == lTimer->mNextTimer)
{
lTimer->mNextTimer = this->mNextTimer;
break;
}
lTimer = lTimer->mNextTimer;
}
}
this->mNextTimer = NULL;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
this->Release();
exit:
return WEAVE_SYSTEM_NO_ERROR;
}
/**
* This method is called by the underlying timer mechanism provided by the platform when the timer fires.
*/
void Timer::HandleComplete()
{
// Save information needed to perform the callback.
Layer& lLayer = this->SystemLayer();
const OnCompleteFunct lOnComplete = this->OnComplete;
void* lAppState = this->AppState;
// Check if timer is armed
VerifyOrExit(lOnComplete != NULL, );
// Atomically disarm if the value has not changed.
VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, NULL), );
// Since this thread changed the state of OnComplete, release the timer.
AppState = NULL;
this->Release();
// Invoke the app's callback, if it's still valid.
if (lOnComplete != NULL)
lOnComplete(&lLayer, lAppState, WEAVE_SYSTEM_NO_ERROR);
exit:
return;
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
/**
* Completes any timers that have expired.
*
* @brief
* A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list
* of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to
* restart the platform timer.
*
* @note
* It's harmless if this API gets called and there are no expired timers.
*
* @return WEAVE_SYSTEM_NO_ERROR on success, error code otherwise.
*
*/
Error Timer::HandleExpiredTimers(Layer& aLayer)
{
// expire each timer in turn until an unexpired timer is reached or the timerlist is emptied.
while (aLayer.mTimerList)
{
const Epoch kCurrentEpoch = Timer::GetCurrentEpoch();
// The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining.
if (Timer::IsEarlierEpoch(aLayer.mTimerList->mAwakenEpoch, kCurrentEpoch + 1))
{
Timer& lTimer = *aLayer.mTimerList;
aLayer.mTimerList = lTimer.mNextTimer;
lTimer.mNextTimer = NULL;
aLayer.mTimerComplete = true;
lTimer.HandleComplete();
aLayer.mTimerComplete = false;
}
else
{
// timers still exist so restart the platform timer.
const uint64_t kDelayMilliseconds = aLayer.mTimerList->mAwakenEpoch - kCurrentEpoch;
/*
* Original kDelayMilliseconds was a 32 bit value. The only way in which this could overflow is if time went backwards
* (e.g. as a result of a time adjustment from time synchronization). Verify that the timer can still be executed
* (even if it is very late) and exit if that is the case. Note: if the time sync ever ends up adjusting the clock, we
* should implement a method that deals with all the timers in the system.
*/
VerifyOrDie(kDelayMilliseconds <= UINT32_MAX);
aLayer.StartPlatformTimer(static_cast<uint32_t>(kDelayMilliseconds));
break; // all remaining timers are still ticking.
}
}
return WEAVE_SYSTEM_NO_ERROR;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
} // namespace System
} // namespace Weave
} // namespace nl