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 std::thread loop_thread(std::bind(&Timer::MainLoop, this));
35 thread_.swap(loop_thread);
36
37 return TIMER_ERR_OK;
38 }
39
Shutdown(bool useJoin)40 void Timer::Shutdown(bool useJoin)
41 {
42 if (reactor_->IsStopped()) {
43 UTILS_LOGD("timer has been stopped already");
44 return;
45 }
46
47 reactor_->StopLoop();
48 if (timeoutMs_ == -1) {
49 std::lock_guard<std::mutex> lock(mutex_);
50 if (intervalToTimers_.empty()) {
51 UTILS_LOGI("no event for epoll wait, use detach to shutdown");
52 thread_.detach();
53 return;
54 }
55 }
56 if (!useJoin) {
57 thread_.detach();
58 return;
59 }
60 thread_.join();
61 }
62
Register(const TimerCallback & callback,uint32_t interval,bool once)63 uint32_t Timer::Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once)
64 {
65 std::lock_guard<std::mutex> lock(mutex_);
66 static std::atomic_uint32_t timerId = 1;
67 int timerFd = once ? INVALID_TIMER_FD : GetTimerFd(interval);
68 if (timerFd == INVALID_TIMER_FD) {
69 uint32_t ret = DoRegister(std::bind(&Timer::OnTimer, this, std::placeholders::_1), interval, once, timerFd);
70 if (ret != TIMER_ERR_OK) {
71 UTILS_LOGE("do register interval timer %{public}d failed, return %{public}u", interval, ret);
72 return TIMER_ERR_DEAL_FAILED;
73 }
74 }
75
76 timerId = GetValidId(timerId);
77 while (timerToEntries_.find(timerId) != timerToEntries_.end()) {
78 timerId++;
79 timerId = GetValidId(timerId);
80 }
81
82 TimerEntryPtr entry(new TimerEntry());
83 entry->timerId = timerId++;
84 entry->interval = interval;
85 entry->callback = callback;
86 entry->once = once;
87 entry->timerFd = timerFd;
88
89 intervalToTimers_[interval].push_back(entry);
90 timerToEntries_[entry->timerId] = entry;
91
92 UTILS_LOGD("register timer %{public}u with %{public}u ms interval.", entry->timerId, entry->interval);
93 return entry->timerId;
94 }
95
Unregister(uint32_t timerId)96 void Timer::Unregister(uint32_t timerId)
97 {
98 std::lock_guard<std::mutex> lock(mutex_);
99 if (timerToEntries_.find(timerId) == timerToEntries_.end()) {
100 UTILS_LOGD("timer %{public}u does not exist", timerId);
101 return;
102 }
103
104 auto entry = timerToEntries_[timerId];
105 UTILS_LOGD("deregister timer %{public}u with %{public}u ms interval", timerId, entry->interval);
106
107 auto itor = intervalToTimers_[entry->interval].begin();
108 for (; itor != intervalToTimers_[entry->interval].end(); ++itor) {
109 if ((*itor)->timerId == timerId) {
110 UTILS_LOGD("erase timer %{public}u.", timerId);
111 if ((*itor)->once) {
112 reactor_->CancelTimer((*itor)->timerFd);
113 timers_.erase((*itor)->timerFd);
114 }
115 intervalToTimers_[entry->interval].erase(itor);
116 break;
117 }
118 }
119
120 if (intervalToTimers_[entry->interval].empty()) {
121 UTILS_LOGD("deregister timer interval: %{public}u.", entry->interval);
122 intervalToTimers_.erase(entry->interval);
123 DoUnregister(entry->interval);
124 }
125 timerToEntries_.erase(timerId);
126 }
127
MainLoop()128 void Timer::MainLoop()
129 {
130 prctl(PR_SET_NAME, name_.c_str(), 0, 0, 0);
131 if (reactor_->StartUp() == TIMER_ERR_OK) {
132 reactor_->RunLoop(timeoutMs_);
133 }
134 reactor_->CleanUp();
135 }
136
DoRegister(const TimerListCallback & callback,uint32_t interval,bool once,int & timerFd)137 uint32_t Timer::DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd)
138 {
139 using namespace std::placeholders;
140 std::function<void(int)> cb = std::bind(&Timer::DoTimerListCallback, this, callback, _1);
141 uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once);
142 if ((ret != TIMER_ERR_OK) || (timerFd < 0)) {
143 UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd);
144 return ret;
145 }
146 timers_[timerFd] = interval;
147 return TIMER_ERR_OK;
148 }
149
DoUnregister(uint32_t interval)150 void Timer::DoUnregister(uint32_t interval)
151 {
152 for (auto& itor : timers_) {
153 if (itor.second == interval) {
154 reactor_->CancelTimer(itor.first);
155 }
156 }
157 }
158
OnTimer(int timerFd)159 void Timer::OnTimer(int timerFd)
160 {
161 uint32_t interval = timers_[timerFd];
162 TimerEntryList entryList;
163 {
164 std::lock_guard<std::mutex> lock(mutex_);
165 entryList = intervalToTimers_[interval];
166 }
167
168 std::vector<uint32_t> onceIdsUnused;
169 for (const TimerEntryPtr& ptr : entryList) {
170 if (ptr->timerFd != timerFd) {
171 continue;
172 }
173 /* if stop, callback is forbidden */
174 if (!reactor_->IsStopped()) {
175 ptr->callback();
176 }
177
178 if (!ptr->once) {
179 continue;
180 }
181 onceIdsUnused.push_back(ptr->timerId);
182 }
183
184 if (!onceIdsUnused.empty()) {
185 EraseUnusedTimerId(interval, onceIdsUnused);
186 }
187 }
188
DoTimerListCallback(const TimerListCallback & callback,int timerFd)189 void Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd)
190 {
191 callback(timerFd);
192 }
193
194 /* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */
GetValidId(uint32_t timerId) const195 uint32_t Timer::GetValidId(uint32_t timerId) const
196 {
197 if (timerId == TIMER_ERR_DEAL_FAILED) {
198 return timerId + 1;
199 }
200 if (timerId == UINT32_MAX) {
201 return 1;
202 }
203 return timerId;
204 }
205
GetTimerFd(uint32_t interval)206 int Timer::GetTimerFd(uint32_t interval /* ms */)
207 {
208 if (intervalToTimers_.find(interval) == intervalToTimers_.end()) {
209 return INVALID_TIMER_FD;
210 }
211 auto &entryList = intervalToTimers_[interval];
212 for (const TimerEntryPtr &ptr : entryList) {
213 if (!ptr->once) {
214 return ptr->timerFd;
215 }
216 }
217 return INVALID_TIMER_FD;
218 }
219
EraseUnusedTimerId(uint32_t interval,const std::vector<uint32_t> & unusedIds)220 void Timer::EraseUnusedTimerId(uint32_t interval, const std::vector<uint32_t>& unusedIds)
221 {
222 std::lock_guard<std::mutex> lock(mutex_);
223 auto &entryList = intervalToTimers_[interval];
224 for (auto itor = entryList.begin(); itor != entryList.end();) {
225 uint32_t id = (*itor)->timerId;
226 if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) {
227 ++itor;
228 continue;
229 }
230
231 reactor_->CancelTimer((*itor)->timerFd);
232 timers_.erase((*itor)->timerFd);
233 itor = entryList.erase(itor);
234 timerToEntries_.erase(id);
235
236 if (entryList.empty()) {
237 intervalToTimers_.erase(interval);
238 DoUnregister(interval);
239 return;
240 }
241 }
242 }
243
244 } // namespace Utils
245 } // namespace OHOS