blob: d9a1600802a10629e70b280a1e36a39f7437c7b3 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2016-2017 Nest Labs, Inc.
*
* 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 chip::System::Timer class, which is used for
* representing an in-progress one-shot timer.
*/
// Include module header
#include <system/SystemTimer.h>
// Include local headers
#include <string.h>
#include <system/SystemError.h>
#include <system/SystemFaultInjection.h>
#include <system/SystemLayer.h>
#include <lib/support/CodeUtils.h>
namespace chip {
namespace System {
#if CHIP_SYSTEM_CONFIG_NUM_TIMERS
/*******************************************************************************
* Timer state
*
* There are two fundamental state-change variables: Object::mSystemLayer and
* Timer::mOnComplete. These must be checked and changed atomically. The state
* of the timer is governed by the following state machine:
*
* INITIAL STATE: mSystemLayer == NULL, mOnComplete == NULL
* |
* V
* UNALLOCATED<-----------------------------+
* | |
* (set mSystemLayer != NULL) |
* | |
* V |
* ALLOCATED-------(set mSystemLayer NULL)--+
* | \-----------------------------+
* | |
* (set mOnComplete != NULL) |
* | |
* V |
* ARMED ---------( clear mOnComplete )--+
*
* When in the ARMED state:
*
* * None of the member variables may mutate.
* * mOnComplete must only be cleared by Cancel() or HandleComplete()
* * Cancel() and HandleComplete() will test that they are the one to
* successfully set mOnComplete NULL. And if so, that will be the
* thread that must call Object::Release().
*
*******************************************************************************
*/
BitMapObjectPool<Timer, CHIP_SYSTEM_CONFIG_NUM_TIMERS> Timer::sPool;
Stats::count_t Timer::mNumInUse = 0;
Stats::count_t Timer::mHighWatermark = 0;
Timer * Timer::New(System::Layer & systemLayer, System::Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState)
{
Timer * timer = Timer::sPool.CreateObject();
if (timer == nullptr)
{
ChipLogError(chipSystemLayer, "Timer pool EMPTY");
}
else
{
timer->mAppState = appState;
timer->mSystemLayer = &systemLayer;
timer->mAwakenTime = SystemClock().GetMonotonicTimestamp() + delay;
if (!__sync_bool_compare_and_swap(&timer->mOnComplete, nullptr, onComplete))
{
chipDie();
}
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
static_assert(CHIP_SYSTEM_CONFIG_NUM_TIMERS < CHIP_SYS_STATS_COUNT_MAX, "Stats count is too small");
if (++mNumInUse > mHighWatermark)
{
mHighWatermark = mNumInUse;
}
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
}
return timer;
}
void Timer::Release()
{
Timer::sPool.ReleaseObject(this);
#if CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
--mNumInUse;
#endif // CHIP_SYSTEM_CONFIG_PROVIDE_STATISTICS
}
void Timer::Clear()
{
TimerCompleteCallback lOnComplete = this->mOnComplete;
// Check if the timer is armed
VerifyOrReturn(lOnComplete != nullptr);
// Atomically disarm if the value has not changed
VerifyOrReturn(__sync_bool_compare_and_swap(&mOnComplete, lOnComplete, nullptr));
// Since this thread changed the state of mOnComplete, release the timer.
mAppState = nullptr;
mSystemLayer = nullptr;
}
void Timer::HandleComplete()
{
// Save information needed to perform the callback.
Layer * lLayer = this->mSystemLayer;
const TimerCompleteCallback lOnComplete = this->mOnComplete;
void * lAppState = this->mAppState;
// Check if timer is armed
VerifyOrReturn(lOnComplete != nullptr, );
// Atomically disarm if the value has not changed.
VerifyOrReturn(__sync_bool_compare_and_swap(&this->mOnComplete, lOnComplete, nullptr), );
// Since this thread changed the state of mOnComplete, release the timer.
mAppState = nullptr;
mSystemLayer = nullptr;
this->Release();
// Invoke the app's callback, if it's still valid.
if (lOnComplete != nullptr)
lOnComplete(lLayer, lAppState);
}
Timer * Timer::List::Add(Timer * add)
{
VerifyOrDie(add != mHead);
if (mHead == NULL || (add->mAwakenTime < mHead->mAwakenTime))
{
add->mNextTimer = mHead;
mHead = add;
}
else
{
Timer * lTimer = mHead;
while (lTimer->mNextTimer)
{
VerifyOrDie(lTimer->mNextTimer != add);
if (add->mAwakenTime < lTimer->mNextTimer->mAwakenTime)
{
// found the insert location.
break;
}
lTimer = lTimer->mNextTimer;
}
add->mNextTimer = lTimer->mNextTimer;
lTimer->mNextTimer = add;
}
return mHead;
}
Timer * Timer::List::Remove(Timer * remove)
{
VerifyOrDie(mHead != nullptr);
if (remove == mHead)
{
mHead = remove->mNextTimer;
}
else
{
Timer * lTimer = mHead;
while (lTimer->mNextTimer)
{
if (remove == lTimer->mNextTimer)
{
lTimer->mNextTimer = remove->mNextTimer;
break;
}
lTimer = lTimer->mNextTimer;
}
}
remove->mNextTimer = nullptr;
return mHead;
}
Timer * Timer::List::Remove(TimerCompleteCallback aOnComplete, void * aAppState)
{
Timer * previous = nullptr;
for (Timer * timer = mHead; timer != nullptr; timer = timer->mNextTimer)
{
if (timer->mOnComplete == aOnComplete && timer->mAppState == aAppState)
{
if (previous == nullptr)
{
mHead = timer->mNextTimer;
}
else
{
previous->mNextTimer = timer->mNextTimer;
}
timer->mNextTimer = nullptr;
return timer;
}
previous = timer;
}
return nullptr;
}
Timer * Timer::List::PopEarliest()
{
if (mHead == nullptr)
{
return nullptr;
}
Timer * earliest = mHead;
mHead = mHead->mNextTimer;
earliest->mNextTimer = nullptr;
return earliest;
}
Timer * Timer::List::PopIfEarlier(Clock::Timestamp t)
{
if ((mHead == nullptr) || !(mHead->mAwakenTime < t))
{
return nullptr;
}
Timer * earliest = mHead;
mHead = mHead->mNextTimer;
earliest->mNextTimer = nullptr;
return earliest;
}
Timer * Timer::List::ExtractEarlier(Clock::Timestamp t)
{
if ((mHead == nullptr) || !(mHead->mAwakenTime < t))
{
return nullptr;
}
Timer * begin = mHead;
Timer * end = mHead;
while ((end->mNextTimer != nullptr) && (end->mNextTimer->mAwakenTime < t))
{
end = end->mNextTimer;
}
mHead = end->mNextTimer;
end->mNextTimer = nullptr;
return begin;
}
CHIP_ERROR Timer::MutexedList::Init()
{
mHead = nullptr;
#if CHIP_SYSTEM_CONFIG_NO_LOCKING
return CHIP_NO_ERROR;
#else // CHIP_SYSTEM_CONFIG_NO_LOCKING
return Mutex::Init(mMutex);
#endif // CHIP_SYSTEM_CONFIG_NO_LOCKING
}
#endif // CHIP_SYSTEM_CONFIG_NUM_TIMERS
} // namespace System
} // namespace chip