13f4cbf05Sopenharmony_ci/*
23f4cbf05Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
33f4cbf05Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
43f4cbf05Sopenharmony_ci * you may not use this file except in compliance with the License.
53f4cbf05Sopenharmony_ci * You may obtain a copy of the License at
63f4cbf05Sopenharmony_ci *
73f4cbf05Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
83f4cbf05Sopenharmony_ci *
93f4cbf05Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
103f4cbf05Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
113f4cbf05Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
123f4cbf05Sopenharmony_ci * See the License for the specific language governing permissions and
133f4cbf05Sopenharmony_ci * limitations under the License.
143f4cbf05Sopenharmony_ci */
153f4cbf05Sopenharmony_ci
163f4cbf05Sopenharmony_ci#include "timer.h"
173f4cbf05Sopenharmony_ci#include "event_reactor.h"
183f4cbf05Sopenharmony_ci
193f4cbf05Sopenharmony_ci#include <algorithm>
203f4cbf05Sopenharmony_ci#include "common_timer_errors.h"
213f4cbf05Sopenharmony_ci#include <atomic>
223f4cbf05Sopenharmony_ci#include <sys/prctl.h>
233f4cbf05Sopenharmony_ci#include "timer_event_handler.h" /* for INVALID_TIMER_FD */
243f4cbf05Sopenharmony_ci#include "utils_log.h"
253f4cbf05Sopenharmony_cinamespace OHOS {
263f4cbf05Sopenharmony_cinamespace Utils {
273f4cbf05Sopenharmony_ci
283f4cbf05Sopenharmony_ciTimer::Timer(const std::string& name, int timeoutMs) : name_(name), timeoutMs_(timeoutMs),
293f4cbf05Sopenharmony_ci    reactor_(new EventReactor())
303f4cbf05Sopenharmony_ci{
313f4cbf05Sopenharmony_ci}
323f4cbf05Sopenharmony_ci
333f4cbf05Sopenharmony_ciTimer::~Timer()
343f4cbf05Sopenharmony_ci{
353f4cbf05Sopenharmony_ci    delete reactor_;
363f4cbf05Sopenharmony_ci}
373f4cbf05Sopenharmony_ci
383f4cbf05Sopenharmony_ciuint32_t Timer::Setup()
393f4cbf05Sopenharmony_ci{
403f4cbf05Sopenharmony_ci    if (thread_.joinable()) { // avoid double assign to an active thread
413f4cbf05Sopenharmony_ci        return TIMER_ERR_INVALID_VALUE;
423f4cbf05Sopenharmony_ci    }
433f4cbf05Sopenharmony_ci    reactor_->SwitchOn();
443f4cbf05Sopenharmony_ci    std::thread loop_thread([this] { this->MainLoop(); });
453f4cbf05Sopenharmony_ci    thread_.swap(loop_thread);
463f4cbf05Sopenharmony_ci
473f4cbf05Sopenharmony_ci    return TIMER_ERR_OK;
483f4cbf05Sopenharmony_ci}
493f4cbf05Sopenharmony_ci
503f4cbf05Sopenharmony_civoid Timer::Shutdown(bool useJoin)
513f4cbf05Sopenharmony_ci{
523f4cbf05Sopenharmony_ci    if (!thread_.joinable()) {
533f4cbf05Sopenharmony_ci        UTILS_LOGD("timer has been stopped already");
543f4cbf05Sopenharmony_ci        return;
553f4cbf05Sopenharmony_ci    }
563f4cbf05Sopenharmony_ci
573f4cbf05Sopenharmony_ci    reactor_->SwitchOff();
583f4cbf05Sopenharmony_ci    if (timeoutMs_ == -1) {
593f4cbf05Sopenharmony_ci        std::lock_guard<std::mutex> lock(mutex_);
603f4cbf05Sopenharmony_ci        if (intervalToTimers_.empty()) {
613f4cbf05Sopenharmony_ci            UTILS_LOGI("no event for epoll wait, use detach to shutdown");
623f4cbf05Sopenharmony_ci
633f4cbf05Sopenharmony_ci            int tmpTimerFd = INVALID_TIMER_FD;
643f4cbf05Sopenharmony_ci            uint32_t ret = reactor_->ScheduleTimer([](int unused) {
653f4cbf05Sopenharmony_ci                UTILS_LOGD("%{public}s:Pseudo-task invoked to get thread exited.", __func__);
663f4cbf05Sopenharmony_ci            }, 0, tmpTimerFd, true); // Add a task to avoid eternally blocking of epoll_wait
673f4cbf05Sopenharmony_ci            if (ret == TIMER_ERR_OK) {
683f4cbf05Sopenharmony_ci                UTILS_LOGD("%{public}s:Pseudo-task need to be scheduled.", __func__);
693f4cbf05Sopenharmony_ci            }
703f4cbf05Sopenharmony_ci
713f4cbf05Sopenharmony_ci            thread_.detach();
723f4cbf05Sopenharmony_ci            return;
733f4cbf05Sopenharmony_ci        }
743f4cbf05Sopenharmony_ci    }
753f4cbf05Sopenharmony_ci    if (!useJoin) {
763f4cbf05Sopenharmony_ci        thread_.detach();
773f4cbf05Sopenharmony_ci        return;
783f4cbf05Sopenharmony_ci    }
793f4cbf05Sopenharmony_ci    thread_.join();
803f4cbf05Sopenharmony_ci}
813f4cbf05Sopenharmony_ci
823f4cbf05Sopenharmony_ciuint32_t Timer::Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once)
833f4cbf05Sopenharmony_ci{
843f4cbf05Sopenharmony_ci    std::lock_guard<std::mutex> lock(mutex_);
853f4cbf05Sopenharmony_ci    static std::atomic_uint32_t timerId = 1;
863f4cbf05Sopenharmony_ci    int timerFd = once ? INVALID_TIMER_FD : GetTimerFd(interval);
873f4cbf05Sopenharmony_ci    if (timerFd == INVALID_TIMER_FD) {
883f4cbf05Sopenharmony_ci        uint32_t ret = DoRegister([this](int fd) { this->OnTimer(fd); }, interval, once, timerFd);
893f4cbf05Sopenharmony_ci        if (ret != TIMER_ERR_OK) {
903f4cbf05Sopenharmony_ci            UTILS_LOGE("do register interval timer %{public}d failed, return %{public}u", interval, ret);
913f4cbf05Sopenharmony_ci            return TIMER_ERR_DEAL_FAILED;
923f4cbf05Sopenharmony_ci        }
933f4cbf05Sopenharmony_ci    }
943f4cbf05Sopenharmony_ci
953f4cbf05Sopenharmony_ci    timerId = GetValidId(timerId);
963f4cbf05Sopenharmony_ci    while (timerToEntries_.find(timerId) != timerToEntries_.end()) {
973f4cbf05Sopenharmony_ci        timerId++;
983f4cbf05Sopenharmony_ci        timerId = GetValidId(timerId);
993f4cbf05Sopenharmony_ci    }
1003f4cbf05Sopenharmony_ci
1013f4cbf05Sopenharmony_ci    TimerEntryPtr entry(new TimerEntry());
1023f4cbf05Sopenharmony_ci    entry->timerId = timerId++;
1033f4cbf05Sopenharmony_ci    entry->interval = interval;
1043f4cbf05Sopenharmony_ci    entry->callback = callback;
1053f4cbf05Sopenharmony_ci    entry->once = once;
1063f4cbf05Sopenharmony_ci    entry->timerFd = timerFd;
1073f4cbf05Sopenharmony_ci
1083f4cbf05Sopenharmony_ci    intervalToTimers_[interval].push_back(entry);
1093f4cbf05Sopenharmony_ci    timerToEntries_[entry->timerId] = entry;
1103f4cbf05Sopenharmony_ci
1113f4cbf05Sopenharmony_ci    UTILS_LOGD("register timer %{public}u with %{public}u ms interval.", entry->timerId, entry->interval);
1123f4cbf05Sopenharmony_ci    return entry->timerId;
1133f4cbf05Sopenharmony_ci}
1143f4cbf05Sopenharmony_ci
1153f4cbf05Sopenharmony_civoid Timer::Unregister(uint32_t timerId)
1163f4cbf05Sopenharmony_ci{
1173f4cbf05Sopenharmony_ci    std::lock_guard<std::mutex> lock(mutex_);
1183f4cbf05Sopenharmony_ci    if (timerToEntries_.find(timerId) == timerToEntries_.end()) {
1193f4cbf05Sopenharmony_ci        UTILS_LOGD("timer %{public}u does not exist", timerId);
1203f4cbf05Sopenharmony_ci        return;
1213f4cbf05Sopenharmony_ci    }
1223f4cbf05Sopenharmony_ci
1233f4cbf05Sopenharmony_ci    auto entry = timerToEntries_[timerId];
1243f4cbf05Sopenharmony_ci    UTILS_LOGD("deregister timer %{public}u with %{public}u ms interval", timerId, entry->interval);
1253f4cbf05Sopenharmony_ci
1263f4cbf05Sopenharmony_ci    auto itor = intervalToTimers_[entry->interval].begin();
1273f4cbf05Sopenharmony_ci    for (; itor != intervalToTimers_[entry->interval].end(); ++itor) {
1283f4cbf05Sopenharmony_ci        if ((*itor)->timerId == timerId) {
1293f4cbf05Sopenharmony_ci            UTILS_LOGD("erase timer %{public}u.", timerId);
1303f4cbf05Sopenharmony_ci            if ((*itor)->once) {
1313f4cbf05Sopenharmony_ci                reactor_->CancelTimer((*itor)->timerFd);
1323f4cbf05Sopenharmony_ci                timers_.erase((*itor)->timerFd);
1333f4cbf05Sopenharmony_ci            }
1343f4cbf05Sopenharmony_ci            intervalToTimers_[entry->interval].erase(itor);
1353f4cbf05Sopenharmony_ci            break;
1363f4cbf05Sopenharmony_ci        }
1373f4cbf05Sopenharmony_ci    }
1383f4cbf05Sopenharmony_ci
1393f4cbf05Sopenharmony_ci    if (intervalToTimers_[entry->interval].empty()) {
1403f4cbf05Sopenharmony_ci        UTILS_LOGD("deregister timer interval: %{public}u.", entry->interval);
1413f4cbf05Sopenharmony_ci        intervalToTimers_.erase(entry->interval);
1423f4cbf05Sopenharmony_ci        DoUnregister(entry->interval);
1433f4cbf05Sopenharmony_ci    }
1443f4cbf05Sopenharmony_ci    timerToEntries_.erase(timerId);
1453f4cbf05Sopenharmony_ci}
1463f4cbf05Sopenharmony_ci
1473f4cbf05Sopenharmony_civoid Timer::MainLoop()
1483f4cbf05Sopenharmony_ci{
1493f4cbf05Sopenharmony_ci    prctl(PR_SET_NAME, name_.c_str(), 0, 0, 0);
1503f4cbf05Sopenharmony_ci    if (reactor_->SetUp() == TIMER_ERR_OK) {
1513f4cbf05Sopenharmony_ci        reactor_->RunLoop(timeoutMs_);
1523f4cbf05Sopenharmony_ci    }
1533f4cbf05Sopenharmony_ci    reactor_->CleanUp();
1543f4cbf05Sopenharmony_ci}
1553f4cbf05Sopenharmony_ci
1563f4cbf05Sopenharmony_ciuint32_t Timer::DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd)
1573f4cbf05Sopenharmony_ci{
1583f4cbf05Sopenharmony_ci    std::function<void(int)> cb = [this, callback](int fd) { this->DoTimerListCallback(callback, fd); };
1593f4cbf05Sopenharmony_ci    uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once);
1603f4cbf05Sopenharmony_ci    if ((ret != TIMER_ERR_OK) || (timerFd < 0)) {
1613f4cbf05Sopenharmony_ci        UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd);
1623f4cbf05Sopenharmony_ci        return ret;
1633f4cbf05Sopenharmony_ci    }
1643f4cbf05Sopenharmony_ci    timers_[timerFd] = interval;
1653f4cbf05Sopenharmony_ci    return TIMER_ERR_OK;
1663f4cbf05Sopenharmony_ci}
1673f4cbf05Sopenharmony_ci
1683f4cbf05Sopenharmony_civoid Timer::DoUnregister(uint32_t interval)
1693f4cbf05Sopenharmony_ci{
1703f4cbf05Sopenharmony_ci    for (auto& itor : timers_) {
1713f4cbf05Sopenharmony_ci        if (itor.second == interval) {
1723f4cbf05Sopenharmony_ci            reactor_->CancelTimer(itor.first);
1733f4cbf05Sopenharmony_ci        }
1743f4cbf05Sopenharmony_ci    }
1753f4cbf05Sopenharmony_ci}
1763f4cbf05Sopenharmony_ci
1773f4cbf05Sopenharmony_civoid Timer::OnTimer(int timerFd)
1783f4cbf05Sopenharmony_ci{
1793f4cbf05Sopenharmony_ci    uint32_t interval;
1803f4cbf05Sopenharmony_ci    TimerEntryList entryList;
1813f4cbf05Sopenharmony_ci    {
1823f4cbf05Sopenharmony_ci        std::lock_guard<std::mutex> lock(mutex_);
1833f4cbf05Sopenharmony_ci        interval = timers_[timerFd];
1843f4cbf05Sopenharmony_ci        entryList = intervalToTimers_[interval];
1853f4cbf05Sopenharmony_ci    }
1863f4cbf05Sopenharmony_ci
1873f4cbf05Sopenharmony_ci    std::vector<uint32_t> onceIdsUnused;
1883f4cbf05Sopenharmony_ci    for (const TimerEntryPtr& ptr : entryList) {
1893f4cbf05Sopenharmony_ci        if (ptr->timerFd != timerFd) {
1903f4cbf05Sopenharmony_ci            continue;
1913f4cbf05Sopenharmony_ci        }
1923f4cbf05Sopenharmony_ci        /* if stop, callback is forbidden */
1933f4cbf05Sopenharmony_ci        if (reactor_->IsLoopReady() && reactor_->IsSwitchedOn()) {
1943f4cbf05Sopenharmony_ci            ptr->callback();
1953f4cbf05Sopenharmony_ci        }
1963f4cbf05Sopenharmony_ci
1973f4cbf05Sopenharmony_ci        if (!ptr->once) {
1983f4cbf05Sopenharmony_ci            continue;
1993f4cbf05Sopenharmony_ci        }
2003f4cbf05Sopenharmony_ci        onceIdsUnused.push_back(ptr->timerId);
2013f4cbf05Sopenharmony_ci    }
2023f4cbf05Sopenharmony_ci
2033f4cbf05Sopenharmony_ci    if (!onceIdsUnused.empty()) {
2043f4cbf05Sopenharmony_ci        EraseUnusedTimerId(interval, onceIdsUnused);
2053f4cbf05Sopenharmony_ci    }
2063f4cbf05Sopenharmony_ci}
2073f4cbf05Sopenharmony_ci
2083f4cbf05Sopenharmony_civoid Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd)
2093f4cbf05Sopenharmony_ci{
2103f4cbf05Sopenharmony_ci    callback(timerFd);
2113f4cbf05Sopenharmony_ci}
2123f4cbf05Sopenharmony_ci
2133f4cbf05Sopenharmony_ci/* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */
2143f4cbf05Sopenharmony_ciuint32_t Timer::GetValidId(uint32_t timerId) const
2153f4cbf05Sopenharmony_ci{
2163f4cbf05Sopenharmony_ci    if (timerId == TIMER_ERR_DEAL_FAILED) {
2173f4cbf05Sopenharmony_ci        return timerId + 1;
2183f4cbf05Sopenharmony_ci    }
2193f4cbf05Sopenharmony_ci    if (timerId == UINT32_MAX) {
2203f4cbf05Sopenharmony_ci        return 1;
2213f4cbf05Sopenharmony_ci    }
2223f4cbf05Sopenharmony_ci    return timerId;
2233f4cbf05Sopenharmony_ci}
2243f4cbf05Sopenharmony_ci
2253f4cbf05Sopenharmony_ciint Timer::GetTimerFd(uint32_t interval /* ms */)
2263f4cbf05Sopenharmony_ci{
2273f4cbf05Sopenharmony_ci    if (intervalToTimers_.find(interval) == intervalToTimers_.end()) {
2283f4cbf05Sopenharmony_ci        return INVALID_TIMER_FD;
2293f4cbf05Sopenharmony_ci    }
2303f4cbf05Sopenharmony_ci    auto &entryList = intervalToTimers_[interval];
2313f4cbf05Sopenharmony_ci    for (const TimerEntryPtr &ptr : entryList) {
2323f4cbf05Sopenharmony_ci        if (!ptr->once) {
2333f4cbf05Sopenharmony_ci            return ptr->timerFd;
2343f4cbf05Sopenharmony_ci        }
2353f4cbf05Sopenharmony_ci    }
2363f4cbf05Sopenharmony_ci    return INVALID_TIMER_FD;
2373f4cbf05Sopenharmony_ci}
2383f4cbf05Sopenharmony_ci
2393f4cbf05Sopenharmony_civoid Timer::EraseUnusedTimerId(uint32_t interval, const std::vector<uint32_t>& unusedIds)
2403f4cbf05Sopenharmony_ci{
2413f4cbf05Sopenharmony_ci    std::lock_guard<std::mutex> lock(mutex_);
2423f4cbf05Sopenharmony_ci    auto &entryList = intervalToTimers_[interval];
2433f4cbf05Sopenharmony_ci    for (auto itor = entryList.begin(); itor != entryList.end();) {
2443f4cbf05Sopenharmony_ci        uint32_t id = (*itor)->timerId;
2453f4cbf05Sopenharmony_ci        if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) {
2463f4cbf05Sopenharmony_ci            ++itor;
2473f4cbf05Sopenharmony_ci            continue;
2483f4cbf05Sopenharmony_ci        }
2493f4cbf05Sopenharmony_ci
2503f4cbf05Sopenharmony_ci        reactor_->CancelTimer((*itor)->timerFd);
2513f4cbf05Sopenharmony_ci        timers_.erase((*itor)->timerFd);
2523f4cbf05Sopenharmony_ci        itor = entryList.erase(itor);
2533f4cbf05Sopenharmony_ci        timerToEntries_.erase(id);
2543f4cbf05Sopenharmony_ci
2553f4cbf05Sopenharmony_ci        if (entryList.empty()) {
2563f4cbf05Sopenharmony_ci            intervalToTimers_.erase(interval);
2573f4cbf05Sopenharmony_ci            DoUnregister(interval);
2583f4cbf05Sopenharmony_ci            return;
2593f4cbf05Sopenharmony_ci        }
2603f4cbf05Sopenharmony_ci    }
2613f4cbf05Sopenharmony_ci}
2623f4cbf05Sopenharmony_ci
2633f4cbf05Sopenharmony_ci} // namespace Utils
2643f4cbf05Sopenharmony_ci} // namespace OHOS
265