• 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     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