1*00c7fec1SAndroid Build Coastguard Worker /*
2*00c7fec1SAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*00c7fec1SAndroid Build Coastguard Worker *
4*00c7fec1SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*00c7fec1SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*00c7fec1SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*00c7fec1SAndroid Build Coastguard Worker *
8*00c7fec1SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*00c7fec1SAndroid Build Coastguard Worker *
10*00c7fec1SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*00c7fec1SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*00c7fec1SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*00c7fec1SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*00c7fec1SAndroid Build Coastguard Worker * limitations under the License.
15*00c7fec1SAndroid Build Coastguard Worker */
16*00c7fec1SAndroid Build Coastguard Worker
17*00c7fec1SAndroid Build Coastguard Worker #include "epoll.h"
18*00c7fec1SAndroid Build Coastguard Worker
19*00c7fec1SAndroid Build Coastguard Worker #include <stdint.h>
20*00c7fec1SAndroid Build Coastguard Worker #include <sys/epoll.h>
21*00c7fec1SAndroid Build Coastguard Worker
22*00c7fec1SAndroid Build Coastguard Worker #include <chrono>
23*00c7fec1SAndroid Build Coastguard Worker #include <functional>
24*00c7fec1SAndroid Build Coastguard Worker #include <map>
25*00c7fec1SAndroid Build Coastguard Worker
26*00c7fec1SAndroid Build Coastguard Worker #include <android-base/logging.h>
27*00c7fec1SAndroid Build Coastguard Worker
28*00c7fec1SAndroid Build Coastguard Worker namespace android {
29*00c7fec1SAndroid Build Coastguard Worker namespace init {
30*00c7fec1SAndroid Build Coastguard Worker
Epoll()31*00c7fec1SAndroid Build Coastguard Worker Epoll::Epoll() {}
32*00c7fec1SAndroid Build Coastguard Worker
Open()33*00c7fec1SAndroid Build Coastguard Worker Result<void> Epoll::Open() {
34*00c7fec1SAndroid Build Coastguard Worker if (epoll_fd_ >= 0) return {};
35*00c7fec1SAndroid Build Coastguard Worker epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
36*00c7fec1SAndroid Build Coastguard Worker
37*00c7fec1SAndroid Build Coastguard Worker if (epoll_fd_ == -1) {
38*00c7fec1SAndroid Build Coastguard Worker return ErrnoError() << "epoll_create1 failed";
39*00c7fec1SAndroid Build Coastguard Worker }
40*00c7fec1SAndroid Build Coastguard Worker return {};
41*00c7fec1SAndroid Build Coastguard Worker }
42*00c7fec1SAndroid Build Coastguard Worker
RegisterHandler(int fd,Handler handler,uint32_t events)43*00c7fec1SAndroid Build Coastguard Worker Result<void> Epoll::RegisterHandler(int fd, Handler handler, uint32_t events) {
44*00c7fec1SAndroid Build Coastguard Worker if (!events) {
45*00c7fec1SAndroid Build Coastguard Worker return Error() << "Must specify events";
46*00c7fec1SAndroid Build Coastguard Worker }
47*00c7fec1SAndroid Build Coastguard Worker
48*00c7fec1SAndroid Build Coastguard Worker auto [it, inserted] = epoll_handlers_.emplace(
49*00c7fec1SAndroid Build Coastguard Worker fd, Info{
50*00c7fec1SAndroid Build Coastguard Worker .handler = std::move(handler),
51*00c7fec1SAndroid Build Coastguard Worker .events = events,
52*00c7fec1SAndroid Build Coastguard Worker });
53*00c7fec1SAndroid Build Coastguard Worker if (!inserted) {
54*00c7fec1SAndroid Build Coastguard Worker return Error() << "Cannot specify two epoll handlers for a given FD";
55*00c7fec1SAndroid Build Coastguard Worker }
56*00c7fec1SAndroid Build Coastguard Worker epoll_event ev = {
57*00c7fec1SAndroid Build Coastguard Worker .events = events,
58*00c7fec1SAndroid Build Coastguard Worker .data.fd = fd,
59*00c7fec1SAndroid Build Coastguard Worker };
60*00c7fec1SAndroid Build Coastguard Worker if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) == -1) {
61*00c7fec1SAndroid Build Coastguard Worker Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
62*00c7fec1SAndroid Build Coastguard Worker epoll_handlers_.erase(fd);
63*00c7fec1SAndroid Build Coastguard Worker return result;
64*00c7fec1SAndroid Build Coastguard Worker }
65*00c7fec1SAndroid Build Coastguard Worker return {};
66*00c7fec1SAndroid Build Coastguard Worker }
67*00c7fec1SAndroid Build Coastguard Worker
UnregisterHandler(int fd)68*00c7fec1SAndroid Build Coastguard Worker Result<void> Epoll::UnregisterHandler(int fd) {
69*00c7fec1SAndroid Build Coastguard Worker if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fd, nullptr) == -1) {
70*00c7fec1SAndroid Build Coastguard Worker return ErrnoError() << "epoll_ctl failed to remove fd";
71*00c7fec1SAndroid Build Coastguard Worker }
72*00c7fec1SAndroid Build Coastguard Worker auto it = epoll_handlers_.find(fd);
73*00c7fec1SAndroid Build Coastguard Worker if (it == epoll_handlers_.end()) {
74*00c7fec1SAndroid Build Coastguard Worker return Error() << "Attempting to remove epoll handler for FD without an existing handler";
75*00c7fec1SAndroid Build Coastguard Worker }
76*00c7fec1SAndroid Build Coastguard Worker to_remove_.insert(it->first);
77*00c7fec1SAndroid Build Coastguard Worker return {};
78*00c7fec1SAndroid Build Coastguard Worker }
79*00c7fec1SAndroid Build Coastguard Worker
SetFirstCallback(std::function<void ()> first_callback)80*00c7fec1SAndroid Build Coastguard Worker void Epoll::SetFirstCallback(std::function<void()> first_callback) {
81*00c7fec1SAndroid Build Coastguard Worker first_callback_ = std::move(first_callback);
82*00c7fec1SAndroid Build Coastguard Worker }
83*00c7fec1SAndroid Build Coastguard Worker
Wait(std::optional<std::chrono::milliseconds> timeout)84*00c7fec1SAndroid Build Coastguard Worker Result<int> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
85*00c7fec1SAndroid Build Coastguard Worker int timeout_ms = -1;
86*00c7fec1SAndroid Build Coastguard Worker if (timeout && timeout->count() < INT_MAX) {
87*00c7fec1SAndroid Build Coastguard Worker timeout_ms = timeout->count();
88*00c7fec1SAndroid Build Coastguard Worker }
89*00c7fec1SAndroid Build Coastguard Worker const auto max_events = epoll_handlers_.size();
90*00c7fec1SAndroid Build Coastguard Worker epoll_event ev[max_events];
91*00c7fec1SAndroid Build Coastguard Worker auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), ev, max_events, timeout_ms));
92*00c7fec1SAndroid Build Coastguard Worker if (num_events == -1) {
93*00c7fec1SAndroid Build Coastguard Worker return ErrnoError() << "epoll_wait failed";
94*00c7fec1SAndroid Build Coastguard Worker }
95*00c7fec1SAndroid Build Coastguard Worker if (num_events > 0 && first_callback_) {
96*00c7fec1SAndroid Build Coastguard Worker first_callback_();
97*00c7fec1SAndroid Build Coastguard Worker }
98*00c7fec1SAndroid Build Coastguard Worker for (int i = 0; i < num_events; ++i) {
99*00c7fec1SAndroid Build Coastguard Worker const auto it = epoll_handlers_.find(ev[i].data.fd);
100*00c7fec1SAndroid Build Coastguard Worker if (it == epoll_handlers_.end()) {
101*00c7fec1SAndroid Build Coastguard Worker continue;
102*00c7fec1SAndroid Build Coastguard Worker }
103*00c7fec1SAndroid Build Coastguard Worker const Info& info = it->second;
104*00c7fec1SAndroid Build Coastguard Worker if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
105*00c7fec1SAndroid Build Coastguard Worker (ev[i].events & EPOLLIN) != ev[i].events) {
106*00c7fec1SAndroid Build Coastguard Worker // This handler wants to know about exception events, and just got one.
107*00c7fec1SAndroid Build Coastguard Worker // Log something informational.
108*00c7fec1SAndroid Build Coastguard Worker LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
109*00c7fec1SAndroid Build Coastguard Worker }
110*00c7fec1SAndroid Build Coastguard Worker info.handler();
111*00c7fec1SAndroid Build Coastguard Worker for (auto fd : to_remove_) {
112*00c7fec1SAndroid Build Coastguard Worker epoll_handlers_.erase(fd);
113*00c7fec1SAndroid Build Coastguard Worker }
114*00c7fec1SAndroid Build Coastguard Worker to_remove_.clear();
115*00c7fec1SAndroid Build Coastguard Worker }
116*00c7fec1SAndroid Build Coastguard Worker return num_events;
117*00c7fec1SAndroid Build Coastguard Worker }
118*00c7fec1SAndroid Build Coastguard Worker
119*00c7fec1SAndroid Build Coastguard Worker } // namespace init
120*00c7fec1SAndroid Build Coastguard Worker } // namespace android
121