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