* Copyright (c) 2025 Huawei Device Co., Ltd.
* 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.
*/
#include <mutex>
#include <chrono>
#include <iostream>
#include "dfx/log/ffrt_log_api.h"
#include "util/ffrt_facade.h"
#include "sync/timer_manager.h"
constexpr uint64_t MAX_TIMER_MS_COUNT = 1000ULL * 100 * 60 * 60 * 24 * 365;
constexpr int TIMER_DESTRUCT_TIMESOUT = 1000;
namespace ffrt {
TimerManager& TimerManager::Instance()
{
static TimerManager ins;
return ins;
}
TimerManager::TimerManager()
{
for (int i = 0; i < QoS::MaxNum(); ++i) {
InitWorkQueAndCb(qos(i));
}
}
TimerManager::~TimerManager()
{
{
std::lock_guard lock(timerMutex_);
teardown = true;
for (auto& [handle, timerData] : timerMap_) {
if (timerData->state == TimerState::NOT_EXECUTED) {
DelayedRemove(timerData->nextDispatchTime, reinterpret_cast<WaitEntry*>(handle));
}
}
}
int try_cnt = TIMER_DESTRUCT_TIMESOUT;
while (try_cnt-- > 0) {
std::unique_lock lock(timerMutex_);
bool hasExecuting = false;
for (auto& [handle, timerData] : timerMap_) {
if (timerData->state == TimerState::EXECUTING) {
hasExecuting = true;
break;
}
}
if (!hasExecuting) {
timerMap_.clear();
break;
}
lock.unlock();
usleep(1000);
}
if (try_cnt <= 0) {
FFRT_SYSEVENT_LOGE("erase timer failed");
}
}
void TimerManager::InitWorkQueAndCb(int qos)
{
workCb[qos] = [this, qos](WaitEntry* we) {
{
std::lock_guard lock(timerMutex_);
if (teardown) {
return;
}
}
int handle = (int)reinterpret_cast<uint64_t>(we);
std::function<void()>&& cbFunc = [this, handle]() {
std::unique_lock timerLock(timerMutex_);
if (teardown) {
return;
}
auto it = timerMap_.find(handle);
if (it == timerMap_.end()) {
return;
}
std::shared_ptr<TimerData> timerMapValue = it->second;
timerMapValue->state = TimerState::EXECUTING;
if (timerMapValue->cb != nullptr) {
timerLock.unlock();
#ifdef FFRT_ENABLE_HITRACE_CHAIN
if (timerMapValue->traceId.valid == HITRACE_ID_VALID) {
TraceChainAdapter::Instance().HiTraceChainRestoreId(&timerMapValue->traceId);
}
#endif
timerMapValue->cb(timerMapValue->data);
#ifdef FFRT_ENABLE_HITRACE_CHAIN
if (timerMapValue->traceId.valid == HITRACE_ID_VALID) {
TraceChainAdapter::Instance().HiTraceChainClearId();
}
#endif
timerLock.lock();
}
timerMapValue->state = TimerState::EXECUTED;
if (timerMapValue->repeat) {
RegisterTimerImpl(timerMapValue);
} else {
timerMap_.erase(it);
}
};
ffrt_task_handle_t taskHandle;
ffrt_dependence_t outDependence = {ffrt_dependence_data, &workQueDeps[qos]};
ffrt_deps_t outDep = {1, &outDependence};
task_attr attr;
attr.qos(qos);
task_attr_private* attrPriv = reinterpret_cast<task_attr_private*>(static_cast<ffrt_task_attr_t*>(&attr));
ffrt_function_header_t* funcWrapper = ffrt::create_function_wrapper(std::move(cbFunc));
bool submitSuccess = FFRTFacade::GetDependenceManager().onSubmit(
false, taskHandle, funcWrapper, nullptr, &outDep, attrPriv, true);
if (unlikely(!submitSuccess)) {
{
std::lock_guard lock(timerMutex_);
if (teardown) {
return;
}
}
FFRT_LOGW("timer retry after %u ms", TIMER_RETRY_INTERVAL);
TimePoint absoluteTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(TIMER_RETRY_INTERVAL);
if (!DelayedWakeup(absoluteTime, reinterpret_cast<WaitEntry*>(handle), workCb[qos], true)) {
FFRT_LOGW("timer retry failed, process may be exiting now");
}
}
};
}
ffrt_timer_t TimerManager::RegisterTimer(int qos, uint64_t timeout, void* data, ffrt_timer_cb cb, bool repeat) noexcept
{
std::lock_guard lock(timerMutex_);
if (teardown) {
return -1;
}
if (timeout > MAX_TIMER_MS_COUNT) {
FFRT_LOGW("timeout exceeds maximum allowed value %llu ms. Clamping to %llu ms.", timeout, MAX_TIMER_MS_COUNT);
timeout = MAX_TIMER_MS_COUNT;
}
std::shared_ptr<TimerData> timerMapValue = std::make_shared<TimerData>(data, cb, repeat, qos, timeout);
timerMapValue->handle = ++timerHandle_;
timerMapValue->state = TimerState::NOT_EXECUTED;
timerMap_.emplace(timerHandle_, timerMapValue);
RegisterTimerImpl(timerMapValue);
return timerHandle_;
}
void TimerManager::RegisterTimerImpl(std::shared_ptr<TimerData> data)
{
TimePoint absoluteTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(data->timeout);
data->nextDispatchTime = absoluteTime;
if (!DelayedWakeup(absoluteTime, reinterpret_cast<WaitEntry*>(data->handle), workCb[data->qos], true)) {
FFRT_LOGW("timer start failed, process may be exiting now");
}
}
int TimerManager::UnregisterTimer(ffrt_timer_t handle) noexcept
{
std::unique_lock timerLock(timerMutex_);
if (teardown) {
return -1;
}
if (handle > timerHandle_ || handle <= -1) {
return -1;
}
auto it = timerMap_.find(handle);
if (it == timerMap_.end()) {
return 0;
}
if (it->second->state == TimerState::NOT_EXECUTED || it->second->state == TimerState::EXECUTED) {
timerMap_.erase(it);
return 0;
}
if (it->second->state == TimerState::EXECUTING) {
while (it->second->state == TimerState::EXECUTING) {
timerLock.unlock();
std::this_thread::yield();
timerLock.lock();
it = timerMap_.find(handle);
if (it == timerMap_.end()) {
return 0;
}
}
timerMap_.erase(it);
return 0;
}
return 0;
}
ffrt_timer_query_t TimerManager::GetTimerStatus(ffrt_timer_t handle) noexcept
{
std::unique_lock timerLock(timerMutex_);
if (teardown) {
return ffrt_timer_notfound;
}
if (handle > timerHandle_) {
return ffrt_timer_notfound;
}
auto it = timerMap_.find(handle);
if (it == timerMap_.end()) {
return ffrt_timer_executed;
}
if (it->second->state == TimerState::NOT_EXECUTED) {
return ffrt_timer_not_executed;
}
if (it->second->state == TimerState::EXECUTING || it->second->state == TimerState::EXECUTED) {
while (it->second->state == TimerState::EXECUTING) {
timerLock.unlock();
std::this_thread::yield();
timerLock.lock();
it = timerMap_.find(handle);
if (it == timerMap_.end()) {
break;
}
}
return ffrt_timer_executed;
}
return ffrt_timer_executed;
}
}