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 <algorithm>
18 #include <cstring>
19 #include <future>
20 #include <list>
21 #include <memory>
22 #include <thread>
23 #include <unistd.h>
24 #include <sys/epoll.h>
25 #include <sys/eventfd.h>
26 #include <sys/timerfd.h>
27 #include "log.h"
28 #include "securec.h"
29
30 namespace utility {
31 class TimerManager {
32 public:
33 static TimerManager &GetInstance();
34
35 bool AddTimer(Timer &timer);
36 void RemoveTimer(Timer &timer);
37
38 private:
39 static const int MAXEPOLLEVENTS = 128;
40 TimerManager();
41 ~TimerManager();
42
43 void Initialize(std::promise<int> startPromise);
44 void OnTimer(std::promise<int> startPromise);
45 void OnCallback(Timer &timer) const;
46
47 int epollFd_ {-1};
48 int stopFd_ {-1};
49 std::mutex mutex_ {};
50 std::list<Timer *> unregisteredList_ {};
51 std::unique_ptr<std::thread> thread_ {};
52 };
53
GetInstance()54 TimerManager &TimerManager::GetInstance()
55 {
56 static TimerManager instance;
57 return instance;
58 }
59
TimerManager()60 TimerManager::TimerManager()
61 {
62 std::promise<int> startPromise;
63 std::future<int> startFuture = startPromise.get_future();
64 thread_ = std::make_unique<std::thread>(&TimerManager::OnTimer, this, std::move(startPromise));
65 startFuture.wait();
66 }
67
~TimerManager()68 TimerManager::~TimerManager()
69 {
70 eventfd_write(stopFd_, 1);
71 if (thread_ && thread_->joinable()) {
72 thread_->join();
73 }
74
75 if (epollFd_ != -1) {
76 close(epollFd_);
77 }
78 if (stopFd_ != -1) {
79 close(stopFd_);
80 }
81 }
82
Initialize(std::promise<int> startPromise)83 void TimerManager::Initialize(std::promise<int> startPromise)
84 {
85 epollFd_ = epoll_create1(EPOLL_CLOEXEC);
86 stopFd_ = eventfd(0, 0);
87 if ((epollFd_ == -1) || (stopFd_ == -1)) {
88 LOG_ERROR("TimerManager: Create epoll failed!!");
89 startPromise.set_value(-1);
90 return;
91 }
92
93 int ret;
94 struct epoll_event event = {};
95
96 event.data.ptr = nullptr;
97 event.events = EPOLLIN;
98 CHECK_EXCEPT_INTR(ret = epoll_ctl(epollFd_, EPOLL_CTL_ADD, stopFd_, &event));
99 if (ret == -1) {
100 LOG_ERROR("TimerManager: Epoll add event failed!!");
101 startPromise.set_value(-1);
102 return;
103 }
104
105 startPromise.set_value(0);
106 }
107
OnCallback(Timer & timer) const108 void TimerManager::OnCallback(Timer &timer) const
109 {
110 uint64_t num = 0;
111 int ret = read(timer.fd_, &num, sizeof(uint64_t));
112 if (ret == sizeof(uint64_t)) {
113 if (num > 1) {
114 LOG_WARN("Timer has expired more than one time.");
115 }
116 timer.callback_();
117 } else if (errno == EAGAIN) {
118 LOG_INFO("Timer is stopped or reset before callback called.");
119 } else {
120 LOG_ERROR("Unknown error type.");
121 }
122 }
123
OnTimer(std::promise<int> startPromise)124 void TimerManager::OnTimer(std::promise<int> startPromise)
125 {
126 Initialize(std::move(startPromise));
127
128 struct epoll_event events[MAXEPOLLEVENTS];
129 for (;;) {
130 {
131 std::lock_guard<std::mutex> lock(mutex_);
132 unregisteredList_.clear();
133 }
134
135 int nfds;
136 CHECK_EXCEPT_INTR(nfds = epoll_wait(epollFd_, events, MAXEPOLLEVENTS, -1));
137 if (nfds == -1) {
138 return;
139 }
140
141 for (int i = 0; i < nfds; ++i) {
142 if (events[i].data.ptr == nullptr) {
143 eventfd_t val;
144 eventfd_read(this->stopFd_, &val);
145 return;
146 }
147 Timer *timer = (Timer *)events[i].data.ptr;
148 if (timer == nullptr) {
149 return;
150 }
151
152 std::unique_lock<std::mutex> lock(mutex_);
153 auto it = std::find(unregisteredList_.begin(), unregisteredList_.end(), timer);
154 if (it != unregisteredList_.end()) {
155 continue;
156 }
157 std::lock_guard<std::mutex> itemLock(timer->mutex_);
158 lock.unlock();
159 OnCallback(*timer);
160 }
161 }
162 }
163
AddTimer(Timer & timer)164 bool TimerManager::AddTimer(Timer &timer)
165 {
166 struct epoll_event event = {};
167 event.data.ptr = &timer;
168 event.events = EPOLLIN | EPOLLRDHUP;
169
170 {
171 std::lock_guard<std::mutex> lock(mutex_);
172 auto it = std::find(unregisteredList_.begin(), unregisteredList_.end(), &timer);
173 if (it != unregisteredList_.end()) {
174 unregisteredList_.remove(&timer);
175 }
176 }
177
178 if (epoll_ctl(epollFd_, EPOLL_CTL_ADD, timer.fd_, &event) == -1) {
179 LOG_ERROR("TimerManager add timer failed");
180 return false;
181 }
182
183 return true;
184 }
185
RemoveTimer(Timer & timer)186 void TimerManager::RemoveTimer(Timer &timer)
187 {
188 if (epoll_ctl(epollFd_, EPOLL_CTL_DEL, timer.fd_, nullptr) == -1) {
189 LOG_ERROR("TimerManager remove timer failed");
190 return;
191 }
192
193 {
194 std::lock_guard<std::mutex> lock(mutex_);
195 unregisteredList_.push_back(&timer);
196 }
197
198 timer.mutex_.lock();
199 timer.mutex_.unlock();
200 }
201
Timer(const std::function<void ()> & callback)202 Timer::Timer(const std::function<void()> &callback)
203 {
204 fd_ = timerfd_create(CLOCK_MONOTONIC, EFD_NONBLOCK);
205 if (fd_ == -1) {
206 LOG_ERROR("timerfd_create ERROR");
207 }
208 callback_ = std::move(callback);
209 if (!TimerManager::GetInstance().AddTimer(*this)) {
210 LOG_ERROR("TimerManager addtimer false");
211 }
212 }
213
~Timer()214 Timer::~Timer()
215 {
216 TimerManager::GetInstance().RemoveTimer(*this);
217 if (close(fd_) == -1) {
218 LOG_ERROR("TimerStop close fd failed");
219 }
220 }
221
Start(int ms,bool isPeriodic)222 bool Timer::Start(int ms, bool isPeriodic)
223 {
224 std::lock_guard<std::mutex> lock(mutex_);
225 struct itimerspec its;
226 (void)memset_s(&its, sizeof(its), 0, sizeof(its));
227
228 its.it_value.tv_sec = ms / MS_PER_SECOND;
229 its.it_value.tv_nsec = (ms % MS_PER_SECOND) * NS_PER_MS;
230 if (isPeriodic) {
231 its.it_interval = its.it_value;
232 }
233
234 if (timerfd_settime(fd_, 0, &its, NULL) == -1) {
235 return false;
236 }
237 return true;
238 }
239
Stop()240 bool Timer::Stop()
241 {
242 std::lock_guard<std::mutex> lock(mutex_);
243 struct itimerspec its;
244 (void)memset_s(&its, sizeof(its), 0, sizeof(its));
245
246 if (timerfd_settime(fd_, 0, &its, NULL) == -1) {
247 return false;
248 }
249 return true;
250 }
251 } // namespace utility