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 #include "event_reactor.h"
18
19 #include "common_timer_errors.h"
20 #include <sys/prctl.h>
21 #include "timer_event_handler.h" /* for INVALID_TIMER_FD */
22 #include "utils_log.h"
23 namespace OHOS {
24 namespace Utils {
25
Timer(const std::string & name,int timeoutMs)26 Timer::Timer(const std::string& name, int timeoutMs) : name_(name), timeoutMs_(timeoutMs),
27 reactor_(new EventReactor())
28 {
29 }
30
~Timer()31 Timer::~Timer()
32 {
33 delete reactor_;
34 }
35
Setup()36 uint32_t Timer::Setup()
37 {
38 if (thread_.joinable()) { // avoid double assign to an active thread
39 return TIMER_ERR_INVALID_VALUE;
40 }
41 reactor_->SwitchOn();
42 std::thread loop_thread([this] { this->MainLoop(); });
43 thread_.swap(loop_thread);
44
45 return TIMER_ERR_OK;
46 }
47
Shutdown(bool useJoin)48 void Timer::Shutdown(bool useJoin)
49 {
50 if (!thread_.joinable()) {
51 UTILS_LOGD("timer has been stopped already");
52 return;
53 }
54
55 reactor_->SwitchOff();
56 if (timeoutMs_ == -1) {
57 std::lock_guard<std::mutex> lock(mutex_);
58 if (intervalToTimers_.empty()) {
59 UTILS_LOGI("no event for epoll wait, use detach to shutdown");
60
61 int tmpTimerFd = INVALID_TIMER_FD;
62 uint32_t ret = reactor_->ScheduleTimer([](int unused) {
63 UTILS_LOGD("%{public}s:Pseudo-task invoked to get thread exited.", __func__);
64 }, 0, tmpTimerFd, true); // Add a task to avoid eternally blocking of epoll_wait
65 if (ret == TIMER_ERR_OK) {
66 UTILS_LOGD("%{public}s:Pseudo-task need to be scheduled.", __func__);
67 }
68
69 thread_.detach();
70 return;
71 }
72 }
73 if (!useJoin) {
74 thread_.detach();
75 return;
76 }
77 thread_.join();
78 }
79
Register(const TimerCallback & callback,uint32_t interval,bool once)80 uint32_t Timer::Register(const TimerCallback& callback, uint32_t interval /* ms */, bool once)
81 {
82 std::lock_guard<std::mutex> lock(mutex_);
83 static std::atomic_uint32_t timerId = 1;
84 int timerFd = once ? INVALID_TIMER_FD : GetTimerFd(interval);
85 if (timerFd == INVALID_TIMER_FD) {
86 uint32_t ret = DoRegister([this](int fd) { this->OnTimer(fd); }, interval, once, timerFd);
87 if (ret != TIMER_ERR_OK) {
88 UTILS_LOGE("do register interval timer %{public}d failed, return %{public}u", interval, ret);
89 return TIMER_ERR_DEAL_FAILED;
90 }
91 }
92
93 timerId = GetValidId(timerId);
94 while (timerToEntries_.find(timerId) != timerToEntries_.end()) {
95 timerId++;
96 timerId = GetValidId(timerId);
97 }
98
99 TimerEntryPtr entry(new TimerEntry());
100 entry->timerId = timerId++;
101 entry->interval = interval;
102 entry->callback = callback;
103 entry->once = once;
104 entry->timerFd = timerFd;
105
106 intervalToTimers_[interval].push_back(entry);
107 timerToEntries_[entry->timerId] = entry;
108
109 UTILS_LOGD("register timer %{public}u with %{public}u ms interval.", entry->timerId, entry->interval);
110 return entry->timerId;
111 }
112
Unregister(uint32_t timerId)113 void Timer::Unregister(uint32_t timerId)
114 {
115 std::lock_guard<std::mutex> lock(mutex_);
116 if (timerToEntries_.find(timerId) == timerToEntries_.end()) {
117 UTILS_LOGD("timer %{public}u does not exist", timerId);
118 return;
119 }
120
121 auto entry = timerToEntries_[timerId];
122 UTILS_LOGD("deregister timer %{public}u with %{public}u ms interval", timerId, entry->interval);
123
124 auto itor = intervalToTimers_[entry->interval].begin();
125 for (; itor != intervalToTimers_[entry->interval].end(); ++itor) {
126 if ((*itor)->timerId == timerId) {
127 UTILS_LOGD("erase timer %{public}u.", timerId);
128 if ((*itor)->once) {
129 reactor_->CancelTimer((*itor)->timerFd);
130 timers_.erase((*itor)->timerFd);
131 }
132 intervalToTimers_[entry->interval].erase(itor);
133 break;
134 }
135 }
136
137 if (intervalToTimers_[entry->interval].empty()) {
138 UTILS_LOGD("deregister timer interval: %{public}u.", entry->interval);
139 intervalToTimers_.erase(entry->interval);
140 DoUnregister(entry->interval);
141 }
142 timerToEntries_.erase(timerId);
143 }
144
MainLoop()145 void Timer::MainLoop()
146 {
147 prctl(PR_SET_NAME, name_.c_str(), 0, 0, 0);
148 if (reactor_->SetUp() == TIMER_ERR_OK) {
149 reactor_->RunLoop(timeoutMs_);
150 }
151 reactor_->CleanUp();
152 }
153
DoRegister(const TimerListCallback & callback,uint32_t interval,bool once,int & timerFd)154 uint32_t Timer::DoRegister(const TimerListCallback& callback, uint32_t interval, bool once, int &timerFd)
155 {
156 std::function<void(int)> cb = [this, callback](int fd) { this->DoTimerListCallback(callback, fd); };
157 uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once);
158 if ((ret != TIMER_ERR_OK) || (timerFd < 0)) {
159 UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd);
160 return ret;
161 }
162 timers_[timerFd] = interval;
163 return TIMER_ERR_OK;
164 }
165
DoUnregister(uint32_t interval)166 void Timer::DoUnregister(uint32_t interval)
167 {
168 for (auto& itor : timers_) {
169 if (itor.second == interval) {
170 reactor_->CancelTimer(itor.first);
171 }
172 }
173 }
174
OnTimer(int timerFd)175 void Timer::OnTimer(int timerFd)
176 {
177 uint32_t interval;
178 TimerEntryList entryList;
179 {
180 std::lock_guard<std::mutex> lock(mutex_);
181 interval = timers_[timerFd];
182 entryList = intervalToTimers_[interval];
183 }
184
185 std::vector<uint32_t> onceIdsUnused;
186 for (const TimerEntryPtr& ptr : entryList) {
187 if (ptr->timerFd != timerFd) {
188 continue;
189 }
190 /* if stop, callback is forbidden */
191 if (reactor_->IsLoopReady() && reactor_->IsSwitchedOn()) {
192 ptr->callback();
193 }
194
195 if (!ptr->once) {
196 continue;
197 }
198 onceIdsUnused.push_back(ptr->timerId);
199 }
200
201 if (!onceIdsUnused.empty()) {
202 EraseUnusedTimerId(interval, onceIdsUnused);
203 }
204 }
205
DoTimerListCallback(const TimerListCallback & callback,int timerFd)206 void Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd)
207 {
208 callback(timerFd);
209 }
210
211 /* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */
GetValidId(uint32_t timerId) const212 uint32_t Timer::GetValidId(uint32_t timerId) const
213 {
214 if (timerId == TIMER_ERR_DEAL_FAILED) {
215 return timerId + 1;
216 }
217 if (timerId == UINT32_MAX) {
218 return 1;
219 }
220 return timerId;
221 }
222
GetTimerFd(uint32_t interval)223 int Timer::GetTimerFd(uint32_t interval /* ms */)
224 {
225 if (intervalToTimers_.find(interval) == intervalToTimers_.end()) {
226 return INVALID_TIMER_FD;
227 }
228 auto &entryList = intervalToTimers_[interval];
229 for (const TimerEntryPtr &ptr : entryList) {
230 if (!ptr->once) {
231 return ptr->timerFd;
232 }
233 }
234 return INVALID_TIMER_FD;
235 }
236
EraseUnusedTimerId(uint32_t interval,const std::vector<uint32_t> & unusedIds)237 void Timer::EraseUnusedTimerId(uint32_t interval, const std::vector<uint32_t>& unusedIds)
238 {
239 std::lock_guard<std::mutex> lock(mutex_);
240 auto &entryList = intervalToTimers_[interval];
241 for (auto itor = entryList.begin(); itor != entryList.end();) {
242 uint32_t id = (*itor)->timerId;
243 if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) {
244 ++itor;
245 continue;
246 }
247
248 reactor_->CancelTimer((*itor)->timerFd);
249 timers_.erase((*itor)->timerFd);
250 itor = entryList.erase(itor);
251 timerToEntries_.erase(id);
252
253 if (entryList.empty()) {
254 intervalToTimers_.erase(interval);
255 DoUnregister(interval);
256 return;
257 }
258 }
259 }
260
261 } // namespace Utils
262 } // namespace OHOS
263