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
149 std::unique_lock<std::mutex> lock(mutex_);
150 auto it = std::find(unregisteredList_.begin(), unregisteredList_.end(), timer);
151 if (it != unregisteredList_.end()) {
152 continue;
153 }
154 std::lock_guard<std::mutex> itemLock(timer->mutex_);
155 lock.unlock();
156 OnCallback(*timer);
157 }
158 }
159 }
160
AddTimer(Timer &timer)161 bool TimerManager::AddTimer(Timer &timer)
162 {
163 struct epoll_event event = {};
164 event.data.ptr = &timer;
165 event.events = EPOLLIN | EPOLLRDHUP;
166
167 {
168 std::lock_guard<std::mutex> lock(mutex_);
169 auto it = std::find(unregisteredList_.begin(), unregisteredList_.end(), &timer);
170 if (it != unregisteredList_.end()) {
171 unregisteredList_.remove(&timer);
172 }
173 }
174
175 if (epoll_ctl(epollFd_, EPOLL_CTL_ADD, timer.fd_, &event) == -1) {
176 LOG_ERROR("TimerManager add timer failed");
177 return false;
178 }
179
180 return true;
181 }
182
RemoveTimer(Timer &timer)183 void TimerManager::RemoveTimer(Timer &timer)
184 {
185 if (epoll_ctl(epollFd_, EPOLL_CTL_DEL, timer.fd_, nullptr) == -1) {
186 LOG_ERROR("TimerManager remove timer failed");
187 return;
188 }
189
190 {
191 std::lock_guard<std::mutex> lock(mutex_);
192 unregisteredList_.push_back(&timer);
193 }
194
195 timer.mutex_.lock();
196 timer.mutex_.unlock();
197 }
198
Timer(const std::function<void()> &callback)199 Timer::Timer(const std::function<void()> &callback)
200 {
201 fd_ = timerfd_create(CLOCK_MONOTONIC, EFD_NONBLOCK);
202 if (fd_ == -1) {
203 LOG_ERROR("timerfd_create ERROR");
204 }
205 callback_ = std::move(callback);
206 if (!TimerManager::GetInstance().AddTimer(*this)) {
207 LOG_ERROR("TimerManager addtimer false");
208 }
209 }
210
~Timer()211 Timer::~Timer()
212 {
213 TimerManager::GetInstance().RemoveTimer(*this);
214 if (close(fd_) == -1) {
215 LOG_ERROR("TimerStop close fd failed");
216 }
217 }
218
Start(int ms, bool isPeriodic)219 bool Timer::Start(int ms, bool isPeriodic)
220 {
221 std::lock_guard<std::mutex> lock(mutex_);
222 struct itimerspec its;
223 (void)memset_s(&its, sizeof(its), 0, sizeof(its));
224
225 its.it_value.tv_sec = ms / MS_PER_SECOND;
226 its.it_value.tv_nsec = (ms % MS_PER_SECOND) * NS_PER_MS;
227 if (isPeriodic) {
228 its.it_interval = its.it_value;
229 }
230
231 if (timerfd_settime(fd_, 0, &its, NULL) == -1) {
232 return false;
233 }
234 return true;
235 }
236
Stop()237 bool Timer::Stop()
238 {
239 std::lock_guard<std::mutex> lock(mutex_);
240 struct itimerspec its;
241 (void)memset_s(&its, sizeof(its), 0, sizeof(its));
242
243 if (timerfd_settime(fd_, 0, &its, NULL) == -1) {
244 return false;
245 }
246 return true;
247 }
248 } // namespace utility