/* * Copyright (c) 2021 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 "timer.h" #include "event_reactor.h" #include #include "common_timer_errors.h" #include #include #include "timer_event_handler.h" /* for INVALID_TIMER_FD */ #include "utils_log.h" namespace OHOS { namespace Utils { Timer::Timer(const std::string& name, int timeoutMs) : name_(name), timeoutMs_(timeoutMs), reactor_(new EventReactor()) { } Timer::~Timer() { delete reactor_; } uint32_t Timer::Setup() { if (thread_.joinable()) { // avoid double assign to an active thread return TIMER_ERR_INVALID_VALUE; } reactor_->SwitchOn(); std::thread loop_thread([this] { this->MainLoop(); }); thread_.swap(loop_thread); return TIMER_ERR_OK; } void Timer::Shutdown(bool useJoin) { if (!thread_.joinable()) { UTILS_LOGD("timer has been stopped already"); return; } reactor_->SwitchOff(); if (timeoutMs_ == -1) { std::lock_guard lock(mutex_); if (intervalToTimers_.empty()) { UTILS_LOGI("no event for epoll wait, use detach to shutdown"); int tmpTimerFd = INVALID_TIMER_FD; uint32_t ret = reactor_->ScheduleTimer([](int unused) { UTILS_LOGD("%{public}s:Pseudo-task invoked to get thread exited.", __func__); }, 0, tmpTimerFd, true); // Add a task to avoid eternally blocking of epoll_wait if (ret == TIMER_ERR_OK) { UTILS_LOGD("%{public}s:Pseudo-task need to be scheduled.", __func__); } thread_.detach(); return; } } if (!useJoin) { thread_.detach(); return; } thread_.join(); } uint32_t Timer::Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once) { std::lock_guard lock(mutex_); static std::atomic_uint32_t timerId = 1; int timerFd = once ? INVALID_TIMER_FD : GetTimerFd(interval); if (timerFd == INVALID_TIMER_FD) { uint32_t ret = DoRegister([this](int fd) { this->OnTimer(fd); }, interval, once, timerFd); if (ret != TIMER_ERR_OK) { UTILS_LOGE("do register interval timer %{public}d failed, return %{public}u", interval, ret); return TIMER_ERR_DEAL_FAILED; } } timerId = GetValidId(timerId); while (timerToEntries_.find(timerId) != timerToEntries_.end()) { timerId++; timerId = GetValidId(timerId); } TimerEntryPtr entry(new TimerEntry()); entry->timerId = timerId++; entry->interval = interval; entry->callback = callback; entry->once = once; entry->timerFd = timerFd; intervalToTimers_[interval].push_back(entry); timerToEntries_[entry->timerId] = entry; UTILS_LOGD("register timer %{public}u with %{public}u ms interval.", entry->timerId, entry->interval); return entry->timerId; } void Timer::Unregister(uint32_t timerId) { std::lock_guard lock(mutex_); if (timerToEntries_.find(timerId) == timerToEntries_.end()) { UTILS_LOGD("timer %{public}u does not exist", timerId); return; } auto entry = timerToEntries_[timerId]; UTILS_LOGD("deregister timer %{public}u with %{public}u ms interval", timerId, entry->interval); auto itor = intervalToTimers_[entry->interval].begin(); for (; itor != intervalToTimers_[entry->interval].end(); ++itor) { if ((*itor)->timerId == timerId) { UTILS_LOGD("erase timer %{public}u.", timerId); if ((*itor)->once) { reactor_->CancelTimer((*itor)->timerFd); timers_.erase((*itor)->timerFd); } intervalToTimers_[entry->interval].erase(itor); break; } } if (intervalToTimers_[entry->interval].empty()) { UTILS_LOGD("deregister timer interval: %{public}u.", entry->interval); intervalToTimers_.erase(entry->interval); DoUnregister(entry->interval); } timerToEntries_.erase(timerId); } void Timer::MainLoop() { prctl(PR_SET_NAME, name_.c_str(), 0, 0, 0); if (reactor_->SetUp() == TIMER_ERR_OK) { reactor_->RunLoop(timeoutMs_); } reactor_->CleanUp(); } uint32_t Timer::DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd) { std::function cb = [this, callback](int fd) { this->DoTimerListCallback(callback, fd); }; uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once); if ((ret != TIMER_ERR_OK) || (timerFd < 0)) { UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd); return ret; } timers_[timerFd] = interval; return TIMER_ERR_OK; } void Timer::DoUnregister(uint32_t interval) { for (auto& itor : timers_) { if (itor.second == interval) { reactor_->CancelTimer(itor.first); } } } void Timer::OnTimer(int timerFd) { uint32_t interval; TimerEntryList entryList; { std::lock_guard lock(mutex_); interval = timers_[timerFd]; entryList = intervalToTimers_[interval]; } std::vector onceIdsUnused; for (const TimerEntryPtr& ptr : entryList) { if (ptr->timerFd != timerFd) { continue; } /* if stop, callback is forbidden */ if (reactor_->IsLoopReady() && reactor_->IsSwitchedOn()) { ptr->callback(); } if (!ptr->once) { continue; } onceIdsUnused.push_back(ptr->timerId); } if (!onceIdsUnused.empty()) { EraseUnusedTimerId(interval, onceIdsUnused); } } void Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd) { callback(timerFd); } /* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */ uint32_t Timer::GetValidId(uint32_t timerId) const { if (timerId == TIMER_ERR_DEAL_FAILED) { return timerId + 1; } if (timerId == UINT32_MAX) { return 1; } return timerId; } int Timer::GetTimerFd(uint32_t interval /* ms */) { if (intervalToTimers_.find(interval) == intervalToTimers_.end()) { return INVALID_TIMER_FD; } auto &entryList = intervalToTimers_[interval]; for (const TimerEntryPtr &ptr : entryList) { if (!ptr->once) { return ptr->timerFd; } } return INVALID_TIMER_FD; } void Timer::EraseUnusedTimerId(uint32_t interval, const std::vector& unusedIds) { std::lock_guard lock(mutex_); auto &entryList = intervalToTimers_[interval]; for (auto itor = entryList.begin(); itor != entryList.end();) { uint32_t id = (*itor)->timerId; if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) { ++itor; continue; } reactor_->CancelTimer((*itor)->timerFd); timers_.erase((*itor)->timerFd); itor = entryList.erase(itor); timerToEntries_.erase(id); if (entryList.empty()) { intervalToTimers_.erase(interval); DoUnregister(interval); return; } } } } // namespace Utils } // namespace OHOS