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