1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "timer.h"
17
18 #include <algorithm>
19 #include "common_timer_errors.h"
20 #include <atomic>
21 #include <sys/prctl.h>
22 #include "timer_event_handler.h" /* for INVALID_TIMER_FD */
23 #include "utils_log.h"
24 namespace OHOS {
25 namespace Utils {
26
Timer(const std::string & name,int timeoutMs)27 Timer::Timer(const std::string& name, int timeoutMs) : name_(name), timeoutMs_(timeoutMs),
28 reactor_(new EventReactor())
29 {
30 }
31
Setup()32 uint32_t Timer::Setup()
33 {
34 if (thread_.joinable()) { // avoid double assign to an active thread
35 return TIMER_ERR_INVALID_VALUE;
36 }
37 reactor_->SwitchOn();
38 std::thread loop_thread(std::bind(&Timer::MainLoop, this));
39 thread_.swap(loop_thread);
40
41 return TIMER_ERR_OK;
42 }
43
Shutdown(bool useJoin)44 void Timer::Shutdown(bool useJoin)
45 {
46 if (!thread_.joinable()) {
47 UTILS_LOGD("timer has been stopped already");
48 return;
49 }
50
51 reactor_->SwitchOff();
52 if (timeoutMs_ == -1) {
53 std::lock_guard<std::mutex> lock(mutex_);
54 if (intervalToTimers_.empty()) {
55 UTILS_LOGI("no event for epoll wait, use detach to shutdown");
56 thread_.detach();
57 return;
58 }
59 }
60 if (!useJoin) {
61 thread_.detach();
62 return;
63 }
64 thread_.join();
65 }
66
Register(const TimerCallback & callback,uint32_t interval,bool once)67 uint32_t Timer::Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once)
68 {
69 std::lock_guard<std::mutex> lock(mutex_);
70 static std::atomic_uint32_t timerId = 1;
71 int timerFd = once ? INVALID_TIMER_FD : GetTimerFd(interval);
72 if (timerFd == INVALID_TIMER_FD) {
73 uint32_t ret = DoRegister(std::bind(&Timer::OnTimer, this, std::placeholders::_1), interval, once, timerFd);
74 if (ret != TIMER_ERR_OK) {
75 UTILS_LOGE("do register interval timer %{public}d failed, return %{public}u", interval, ret);
76 return TIMER_ERR_DEAL_FAILED;
77 }
78 }
79
80 timerId = GetValidId(timerId);
81 while (timerToEntries_.find(timerId) != timerToEntries_.end()) {
82 timerId++;
83 timerId = GetValidId(timerId);
84 }
85
86 TimerEntryPtr entry(new TimerEntry());
87 entry->timerId = timerId++;
88 entry->interval = interval;
89 entry->callback = callback;
90 entry->once = once;
91 entry->timerFd = timerFd;
92
93 intervalToTimers_[interval].push_back(entry);
94 timerToEntries_[entry->timerId] = entry;
95
96 UTILS_LOGD("register timer %{public}u with %{public}u ms interval.", entry->timerId, entry->interval);
97 return entry->timerId;
98 }
99
Unregister(uint32_t timerId)100 void Timer::Unregister(uint32_t timerId)
101 {
102 std::lock_guard<std::mutex> lock(mutex_);
103 if (timerToEntries_.find(timerId) == timerToEntries_.end()) {
104 UTILS_LOGD("timer %{public}u does not exist", timerId);
105 return;
106 }
107
108 auto entry = timerToEntries_[timerId];
109 UTILS_LOGD("deregister timer %{public}u with %{public}u ms interval", timerId, entry->interval);
110
111 auto itor = intervalToTimers_[entry->interval].begin();
112 for (; itor != intervalToTimers_[entry->interval].end(); ++itor) {
113 if ((*itor)->timerId == timerId) {
114 UTILS_LOGD("erase timer %{public}u.", timerId);
115 if ((*itor)->once) {
116 reactor_->CancelTimer((*itor)->timerFd);
117 timers_.erase((*itor)->timerFd);
118 }
119 intervalToTimers_[entry->interval].erase(itor);
120 break;
121 }
122 }
123
124 if (intervalToTimers_[entry->interval].empty()) {
125 UTILS_LOGD("deregister timer interval: %{public}u.", entry->interval);
126 intervalToTimers_.erase(entry->interval);
127 DoUnregister(entry->interval);
128 }
129 timerToEntries_.erase(timerId);
130 }
131
MainLoop()132 void Timer::MainLoop()
133 {
134 prctl(PR_SET_NAME, name_.c_str(), 0, 0, 0);
135 if (reactor_->SetUp() == TIMER_ERR_OK) {
136 reactor_->RunLoop(timeoutMs_);
137 }
138 reactor_->CleanUp();
139 }
140
DoRegister(const TimerListCallback & callback,uint32_t interval,bool once,int & timerFd)141 uint32_t Timer::DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd)
142 {
143 using namespace std::placeholders;
144 std::function<void(int)> cb = std::bind(&Timer::DoTimerListCallback, this, callback, _1);
145 uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once);
146 if ((ret != TIMER_ERR_OK) || (timerFd < 0)) {
147 UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd);
148 return ret;
149 }
150 timers_[timerFd] = interval;
151 return TIMER_ERR_OK;
152 }
153
DoUnregister(uint32_t interval)154 void Timer::DoUnregister(uint32_t interval)
155 {
156 for (auto& itor : timers_) {
157 if (itor.second == interval) {
158 reactor_->CancelTimer(itor.first);
159 }
160 }
161 }
162
OnTimer(int timerFd)163 void Timer::OnTimer(int timerFd)
164 {
165 uint32_t interval;
166 TimerEntryList entryList;
167 {
168 std::lock_guard<std::mutex> lock(mutex_);
169 interval = timers_[timerFd];
170 entryList = intervalToTimers_[interval];
171 }
172
173 std::vector<uint32_t> onceIdsUnused;
174 for (const TimerEntryPtr& ptr : entryList) {
175 if (ptr->timerFd != timerFd) {
176 continue;
177 }
178 /* if stop, callback is forbidden */
179 if (reactor_->IsLoopReady() && reactor_->IsSwitchedOn()) {
180 ptr->callback();
181 }
182
183 if (!ptr->once) {
184 continue;
185 }
186 onceIdsUnused.push_back(ptr->timerId);
187 }
188
189 if (!onceIdsUnused.empty()) {
190 EraseUnusedTimerId(interval, onceIdsUnused);
191 }
192 }
193
DoTimerListCallback(const TimerListCallback & callback,int timerFd)194 void Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd)
195 {
196 callback(timerFd);
197 }
198
199 /* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */
GetValidId(uint32_t timerId) const200 uint32_t Timer::GetValidId(uint32_t timerId) const
201 {
202 if (timerId == TIMER_ERR_DEAL_FAILED) {
203 return timerId + 1;
204 }
205 if (timerId == UINT32_MAX) {
206 return 1;
207 }
208 return timerId;
209 }
210
GetTimerFd(uint32_t interval)211 int Timer::GetTimerFd(uint32_t interval /* ms */)
212 {
213 if (intervalToTimers_.find(interval) == intervalToTimers_.end()) {
214 return INVALID_TIMER_FD;
215 }
216 auto &entryList = intervalToTimers_[interval];
217 for (const TimerEntryPtr &ptr : entryList) {
218 if (!ptr->once) {
219 return ptr->timerFd;
220 }
221 }
222 return INVALID_TIMER_FD;
223 }
224
EraseUnusedTimerId(uint32_t interval,const std::vector<uint32_t> & unusedIds)225 void Timer::EraseUnusedTimerId(uint32_t interval, const std::vector<uint32_t>& unusedIds)
226 {
227 std::lock_guard<std::mutex> lock(mutex_);
228 auto &entryList = intervalToTimers_[interval];
229 for (auto itor = entryList.begin(); itor != entryList.end();) {
230 uint32_t id = (*itor)->timerId;
231 if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) {
232 ++itor;
233 continue;
234 }
235
236 reactor_->CancelTimer((*itor)->timerFd);
237 timers_.erase((*itor)->timerFd);
238 itor = entryList.erase(itor);
239 timerToEntries_.erase(id);
240
241 if (entryList.empty()) {
242 intervalToTimers_.erase(interval);
243 DoUnregister(interval);
244 return;
245 }
246 }
247 }
248
249 } // namespace Utils
250 } // namespace OHOS
251