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