• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(std::bind(&Timer::MainLoop, this));
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(std::bind(&Timer::OnTimer, this, std::placeholders::_1), 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     using namespace std::placeholders;
159     std::function<void(int)> cb = std::bind(&Timer::DoTimerListCallback, this, callback, _1);
160     uint32_t ret = reactor_->ScheduleTimer(cb, interval, timerFd, once);
161     if ((ret != TIMER_ERR_OK) || (timerFd < 0)) {
162         UTILS_LOGE("ScheduleTimer failed!ret:%{public}d, timerFd:%{public}d", ret, timerFd);
163         return ret;
164     }
165     timers_[timerFd] = interval;
166     return TIMER_ERR_OK;
167 }
168 
DoUnregister(uint32_t interval)169 void Timer::DoUnregister(uint32_t interval)
170 {
171     for (auto& itor : timers_) {
172         if (itor.second == interval) {
173             reactor_->CancelTimer(itor.first);
174         }
175     }
176 }
177 
OnTimer(int timerFd)178 void Timer::OnTimer(int timerFd)
179 {
180     uint32_t interval;
181     TimerEntryList entryList;
182     {
183         std::lock_guard<std::mutex> lock(mutex_);
184         interval = timers_[timerFd];
185         entryList = intervalToTimers_[interval];
186     }
187 
188     std::vector<uint32_t> onceIdsUnused;
189     for (const TimerEntryPtr& ptr : entryList) {
190         if (ptr->timerFd != timerFd) {
191             continue;
192         }
193         /* if stop, callback is forbidden */
194         if (reactor_->IsLoopReady() && reactor_->IsSwitchedOn()) {
195             ptr->callback();
196         }
197 
198         if (!ptr->once) {
199             continue;
200         }
201         onceIdsUnused.push_back(ptr->timerId);
202     }
203 
204     if (!onceIdsUnused.empty()) {
205         EraseUnusedTimerId(interval, onceIdsUnused);
206     }
207 }
208 
DoTimerListCallback(const TimerListCallback & callback,int timerFd)209 void Timer::DoTimerListCallback(const TimerListCallback& callback, int timerFd)
210 {
211     callback(timerFd);
212 }
213 
214 /* valid range: [1, UINT32_MAX], but not TIMER_ERR_DEAL_FAILED */
GetValidId(uint32_t timerId) const215 uint32_t Timer::GetValidId(uint32_t timerId) const
216 {
217     if (timerId == TIMER_ERR_DEAL_FAILED) {
218         return timerId + 1;
219     }
220     if (timerId == UINT32_MAX) {
221         return 1;
222     }
223     return timerId;
224 }
225 
GetTimerFd(uint32_t interval)226 int Timer::GetTimerFd(uint32_t interval /* ms */)
227 {
228     if (intervalToTimers_.find(interval) == intervalToTimers_.end()) {
229         return INVALID_TIMER_FD;
230     }
231     auto &entryList = intervalToTimers_[interval];
232     for (const TimerEntryPtr &ptr : entryList) {
233         if (!ptr->once) {
234             return ptr->timerFd;
235         }
236     }
237     return INVALID_TIMER_FD;
238 }
239 
EraseUnusedTimerId(uint32_t interval,const std::vector<uint32_t> & unusedIds)240 void Timer::EraseUnusedTimerId(uint32_t interval, const std::vector<uint32_t>& unusedIds)
241 {
242     std::lock_guard<std::mutex> lock(mutex_);
243     auto &entryList = intervalToTimers_[interval];
244     for (auto itor = entryList.begin(); itor != entryList.end();) {
245         uint32_t id = (*itor)->timerId;
246         if (std::find(unusedIds.begin(), unusedIds.end(), id) == unusedIds.end()) {
247             ++itor;
248             continue;
249         }
250 
251         reactor_->CancelTimer((*itor)->timerFd);
252         timers_.erase((*itor)->timerFd);
253         itor = entryList.erase(itor);
254         timerToEntries_.erase(id);
255 
256         if (entryList.empty()) {
257             intervalToTimers_.erase(interval);
258             DoUnregister(interval);
259             return;
260         }
261     }
262 }
263 
264 } // namespace Utils
265 } // namespace OHOS
266