/* * 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. */ #ifndef UTILS_TIMER_H #define UTILS_TIMER_H #include #include #include #include #include #include #include #include #include "../src/event_reactor.h" namespace OHOS { namespace Utils { /* * Notice: * 1. Timer should be set up(via Setup()) before use, and shutdown(via Shutdown()) before its deconstruction. * * 2. Timer should be set up first and then shutdown. * Avoid delegating them to different threads since it may cause multithreading problem. * * 3. Set up Timer again would not reset this Timer, but return `TIMER_ERR_INVALID_VALUE`. * If a reset operation is required, shut Timer down first and then set it up. * * 4. Parameter in Shutdown() determines whether the thread in Timer would be detach or join. * (True(default) --> join; False --> detach). * + Detach operation would cause possible multithreading problems, thus is not recommended. * If a detach operation is required, availability of related objects used in `thread_` should be guaranteed. */ class Timer { public: using TimerCallback = std::function; using TimerCallbackPtr = std::shared_ptr; using TimerListCallback = std::function; public: /* * if performance-sensitive, change "timeout" larger before Setup * default-value(1000ms), performance-estimate: occupy fixed-100us in every default-value(1000ms) * timeout: range [-1, INT32MAX], but [-1,0] is not recommended * -1: wait for ever(until event-trigger); * 0: no wait, occupy too much cpu time; * others: wait(until event-trigger) */ explicit Timer(const std::string& name, int timeoutMs = 1000); virtual ~Timer() {} virtual uint32_t Setup(); /* * useJoin true: use std::thread::join(default) * false: use std::thread::detach(not recommended) * if timeoutMs = -1 and no valid event-trigger in epoll_wait: * use std::thread::detach inside to avoid deadloop */ virtual void Shutdown(bool useJoin = true); uint32_t Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once = false); void Unregister(uint32_t timerId); private: void MainLoop(); void OnTimer(int timerFd); virtual uint32_t DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd); virtual void DoUnregister(uint32_t interval); void DoTimerListCallback(const TimerListCallback& callback, int timerFd); uint32_t GetValidId(uint32_t timerId) const; int GetTimerFd(uint32_t interval /* ms */); void EraseUnusedTimerId(uint32_t interval, const std::vector& unusedIds); private: struct TimerEntry { uint32_t timerId; // unique id uint32_t interval; // million second TimerCallback callback; bool once; int timerFd; }; using TimerEntryPtr = std::shared_ptr; using TimerEntryList = std::list; std::map intervalToTimers_; // interval to TimerEntryList std::map timerToEntries_; // timer_id to TimerEntry std::string name_; int timeoutMs_; std::thread thread_; std::unique_ptr reactor_; std::map timers_; // timer_fd to interval std::mutex mutex_; }; } // namespace Utils } // namespace OHOS #endif