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