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