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_event_handler.h"
17 #include "event_reactor.h"
18 #include "common_timer_errors.h"
19 #include "utils_log.h"
20
21 #include <sys/timerfd.h>
22
23 namespace OHOS {
24 namespace Utils {
25
26 // Unit of measure conversion
27 static const int MILLI_TO_BASE = 1000;
28 static const int NANO_TO_BASE = 1000000000;
29 constexpr int MILLI_TO_NANO = NANO_TO_BASE / MILLI_TO_BASE;
30
TimerEventHandler(EventReactor * p,uint32_t timeout,bool once)31 TimerEventHandler::TimerEventHandler(EventReactor* p, uint32_t timeout /* ms */, bool once)
32 : EventHandler(timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC), p),
33 once_(once),
34 interval_(timeout),
35 callback_()
36 {
37 }
38
~TimerEventHandler()39 TimerEventHandler::~TimerEventHandler()
40 {
41 close(GetHandle());
42 SetHandle(INVALID_TIMER_FD);
43 }
44
Initialize()45 uint32_t TimerEventHandler::Initialize()
46 {
47 if ((GetHandle() == INVALID_TIMER_FD)) {
48 UTILS_LOGE("TimerEventHandler::initialize failed.");
49 return TIMER_ERR_INVALID_VALUE;
50 }
51
52 struct itimerspec newValue = {{0, 0}, {0, 0}};
53 timespec now{0, 0};
54 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
55 UTILS_LOGE("Failed clock_gettime.");
56 return TIMER_ERR_DEAL_FAILED;
57 }
58
59 // next time out time is now + interval
60 newValue.it_value.tv_sec = now.tv_sec + interval_ / MILLI_TO_BASE;
61 newValue.it_value.tv_nsec = now.tv_nsec + (interval_ % MILLI_TO_BASE) * MILLI_TO_NANO;
62 if (newValue.it_value.tv_nsec >= NANO_TO_BASE) {
63 newValue.it_value.tv_sec += 1;
64 newValue.it_value.tv_nsec = newValue.it_value.tv_nsec % NANO_TO_BASE;
65 }
66
67 if (once_) {
68 // interval, 0 means time out only once
69 newValue.it_interval.tv_sec = 0;
70 newValue.it_interval.tv_nsec = 0;
71 } else {
72 // interval
73 newValue.it_interval.tv_sec = interval_ / MILLI_TO_BASE;
74 newValue.it_interval.tv_nsec = (interval_ % MILLI_TO_BASE) * MILLI_TO_NANO;
75 }
76
77 if (timerfd_settime(GetHandle(), TFD_TIMER_ABSTIME, &newValue, nullptr) == -1) {
78 UTILS_LOGE("Failed in timerFd_settime");
79 return TIMER_ERR_DEAL_FAILED;
80 }
81
82 SetReadCallback([this] { this->TimeOut(); });
83 EnableRead();
84 return TIMER_ERR_OK;
85 }
86
Uninitialize()87 void TimerEventHandler::Uninitialize()
88 {
89 DisableAll();
90 }
91
TimeOut()92 void TimerEventHandler::TimeOut()
93 {
94 if (GetHandle() == INVALID_TIMER_FD) {
95 UTILS_LOGE("timerFd_ is invalid.");
96 return;
97 }
98 uint64_t expirations = 0;
99 ssize_t n = ::read(GetHandle(), &expirations, sizeof(expirations));
100 if (n != sizeof(expirations)) {
101 int erronRead = errno;
102 struct itimerspec current = {
103 .it_interval = {.tv_sec = -1, .tv_nsec = -1},
104 .it_value = {.tv_sec = -1, .tv_nsec = -1}
105 };
106 if (timerfd_gettime(GetHandle(), ¤t) == -1) {
107 UTILS_LOGE("timerfd_gettime failed, errno=%{public}d", errno);
108 }
109 UTILS_LOGE("epoll_loop::on_timer() reads %{public}d bytes instead of 8, timerFd=%{public}d, errno=%{public}d, "
110 "Current timer value: %{public}lld sec, %{public}ld nsec", static_cast<int>(n), GetHandle(),
111 erronRead, static_cast<long long>(current.it_value.tv_sec), current.it_value.tv_nsec);
112 }
113 if (callback_) {
114 callback_(GetHandle());
115 }
116 }
117
118 } // namespace Utils
119 } // namespace OHOS
120