1/*
2 * Copyright (c) 2023 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#include <cstdio>
16#include <cstring>
17#include <queue>
18#include <vector>
19#include <sys/epoll.h>
20#include <unistd.h>
21#include "utils_log.h"
22#include "io_event_common.h"
23#include "common_event_sys_errors.h"
24#include "io_event_epoll.h"
25
26namespace OHOS {
27namespace Utils {
28IOEventEpoll::IOEventEpoll()
29    : epollFd_(epoll_create1(EPOLL_CLOEXEC)), maxEvents_(EPOLL_MAX_EVENTS_INIT) {}
30
31IOEventEpoll::~IOEventEpoll()
32{
33    CleanUp();
34}
35
36ErrCode IOEventEpoll::SetUp()
37{
38    if (epollFd_ < 0) {
39        epollFd_ = epoll_create1(EPOLL_CLOEXEC);
40        if (epollFd_ < 0) {
41            return EVENT_SYS_ERR_BADF;
42        }
43    }
44    return EVENT_SYS_ERR_OK;
45}
46
47void IOEventEpoll::CleanUp()
48{
49    if (epollFd_ != IO_EVENT_INVALID_FD) {
50        if (close(epollFd_) != 0) {
51            UTILS_LOGW("%{public}s: Failed, cannot close fd: %{public}s.", __FUNCTION__, strerror(errno));
52        }
53        epollFd_ = IO_EVENT_INVALID_FD;
54    }
55}
56
57bool IOEventEpoll::OperateEpoll(int op, int fd, EPEventId epollEvents)
58{
59    struct epoll_event event;
60    bzero(&event, sizeof(event));
61    event.events = epollEvents;
62    event.data.fd = fd;
63
64    if (epoll_ctl(epollFd_, op, fd, &event) != 0) {
65        UTILS_LOGE("%{public}s: Operate on epoll failed, %{public}s. epoll_fd: %{public}d , operation: %{public}d, \
66                   target fd: %{public}d", __FUNCTION__, strerror(errno), epollFd_, op, fd);
67        return false;
68    }
69
70    switch (op) {
71        case EPOLL_CTL_ADD:
72            interestFds_.insert(fd);
73            break;
74        case EPOLL_CTL_DEL:
75            interestFds_.erase(fd);
76            break;
77        default:
78            break;
79    }
80    return true;
81}
82
83ErrCode IOEventEpoll::ModifyEvents(int fd, REventId events)
84{
85    if (fd == -1) {
86        UTILS_LOGE("%{public}s: Failed, bad fd.", __FUNCTION__);
87        return EVENT_SYS_ERR_BADF;
88    }
89
90    int op = EPOLL_CTL_ADD;
91    if (interestFds_.find(fd) != interestFds_.end()) {
92        if (events == Events::EVENT_NONE) {
93            op = EPOLL_CTL_DEL;
94        } else {
95            op = EPOLL_CTL_MOD;
96        }
97    }
98
99    if (!OperateEpoll(op, fd, Reactor2Epoll(events))) {
100        UTILS_LOGE("%{public}s: Modify events failed.", __FUNCTION__);
101        return EVENT_SYS_ERR_FAILED;
102    }
103    return EVENT_SYS_ERR_OK;
104}
105
106ErrCode IOEventEpoll::Polling(int timeout /* ms */, std::vector<std::pair<int, REventId>>& res)
107{
108    struct epoll_event epollEvents[maxEvents_];
109    int nfds = epoll_wait(epollFd_, &epollEvents[0], maxEvents_, timeout);
110    if (nfds == 0) {
111        return EVENT_SYS_ERR_NOEVENT;
112    }
113    if (nfds == -1) {
114        UTILS_LOGE("%{public}s: epoll_wait() failed, %{public}s", __FUNCTION__, strerror(errno));
115        return EVENT_SYS_ERR_FAILED;
116    }
117    for (int idx = 0; idx < nfds; ++idx) {
118        res.emplace_back(std::make_pair(epollEvents[idx].data.fd, Epoll2Reactor(epollEvents[idx].events)));
119    }
120
121    if (nfds == maxEvents_) {
122        maxEvents_ *= EXPANSION_COEFF;
123    }
124    return EVENT_SYS_ERR_OK;
125}
126
127REventId IOEventEpoll::Epoll2Reactor(EPEventId epollEvents)
128{
129    REventId res = Events::EVENT_NONE;
130    if ((epollEvents & EPOLLHUP) && !(epollEvents & EPOLLIN)) {
131        res |= Events::EVENT_CLOSE;
132    }
133
134    if (epollEvents & EPOLLERR) {
135        res |= Events::EVENT_ERROR;
136    }
137
138    if (epollEvents & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {
139        res |= Events::EVENT_READ;
140    }
141
142    if (epollEvents & EPOLLOUT) {
143        res |= Events::EVENT_WRITE;
144    }
145
146    return res;
147}
148
149EPEventId IOEventEpoll::Reactor2Epoll(REventId reactorEvent)
150{
151    EPEventId res = 0u;
152
153    if (reactorEvent & Events::EVENT_READ) {
154        res |= EPOLLIN | EPOLLPRI;
155    }
156
157    if (reactorEvent & Events::EVENT_WRITE) {
158        res |= EPOLLOUT;
159    }
160
161    if (reactorEvent & Events::EVENT_ERROR) {
162        res |= EPOLLERR;
163    }
164
165    return res;
166}
167
168} // namespace Utils
169} // namespace OHOS
170