1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "IOEventLoop.h" 18 19 #include <event2/event.h> 20 #include <fcntl.h> 21 22 #include <android-base/logging.h> 23 24 struct IOEvent { 25 IOEventLoop* loop; 26 event* e; 27 timeval timeout; 28 std::function<bool()> callback; 29 bool enabled; 30 31 IOEvent(IOEventLoop* loop, const std::function<bool()>& callback) 32 : loop(loop), e(nullptr), timeout({}), callback(callback), enabled(false) { 33 } 34 35 ~IOEvent() { 36 if (e != nullptr) { 37 event_free(e); 38 } 39 } 40 }; 41 42 IOEventLoop::IOEventLoop() 43 : ebase_(nullptr), has_error_(false), use_precise_timer_(false), in_loop_(false) {} 44 45 IOEventLoop::~IOEventLoop() { 46 events_.clear(); 47 if (ebase_ != nullptr) { 48 event_base_free(ebase_); 49 } 50 } 51 52 bool IOEventLoop::UsePreciseTimer() { 53 if (ebase_ != nullptr) { 54 return false; // Too late to set the flag. 55 } 56 use_precise_timer_ = true; 57 return true; 58 } 59 60 bool IOEventLoop::EnsureInit() { 61 if (ebase_ == nullptr) { 62 event_config* cfg = event_config_new(); 63 if (cfg != nullptr) { 64 if (use_precise_timer_) { 65 event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER); 66 } 67 if (event_config_avoid_method(cfg, "epoll") != 0) { 68 LOG(ERROR) << "event_config_avoid_method"; 69 return false; 70 } 71 ebase_ = event_base_new_with_config(cfg); 72 // perf event files support reporting available data via poll methods. However, it doesn't 73 // work well with epoll. Because perf_poll() in kernel/events/core.c uses a report and reset 74 // way to report poll events. If perf_poll() is called twice, it may return POLLIN for the 75 // first time, and no events for the second time. And epoll may call perf_poll() more than 76 // once to confirm events. A failed situation is below: 77 // When profiling SimpleperfExampleOfKotlin on Pixel device with `-g --duration 10`, the 78 // kernel fills up the buffer before we call epoll_ctl(EPOLL_CTL_ADD). Then the POLLIN event 79 // is returned when calling epoll_ctl(), while no events are returned when calling 80 // epoll_wait(). As a result, simpleperf doesn't receive any poll wakeup events. 81 if (strcmp(event_base_get_method(ebase_), "poll") != 0) { 82 LOG(ERROR) << "event_base_get_method isn't poll: " << event_base_get_method(ebase_); 83 return false; 84 } 85 event_config_free(cfg); 86 } 87 if (ebase_ == nullptr) { 88 LOG(ERROR) << "failed to create event_base"; 89 return false; 90 } 91 } 92 return true; 93 } 94 95 void IOEventLoop::EventCallbackFn(int, int16_t, void* arg) { 96 IOEvent* e = static_cast<IOEvent*>(arg); 97 if (!e->callback()) { 98 e->loop->has_error_ = true; 99 e->loop->ExitLoop(); 100 } 101 } 102 103 static bool MakeFdNonBlocking(int fd) { 104 int flags = fcntl(fd, F_GETFL, 0); 105 if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 106 PLOG(ERROR) << "fcntl() failed"; 107 return false; 108 } 109 return true; 110 } 111 112 IOEventRef IOEventLoop::AddReadEvent(int fd, 113 const std::function<bool()>& callback) { 114 if (!MakeFdNonBlocking(fd)) { 115 return nullptr; 116 } 117 return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback); 118 } 119 120 IOEventRef IOEventLoop::AddWriteEvent(int fd, 121 const std::function<bool()>& callback) { 122 if (!MakeFdNonBlocking(fd)) { 123 return nullptr; 124 } 125 return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback); 126 } 127 128 bool IOEventLoop::AddSignalEvent(int sig, 129 const std::function<bool()>& callback) { 130 return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr; 131 } 132 133 bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, 134 const std::function<bool()>& callback) { 135 for (auto sig : sigs) { 136 if (!AddSignalEvent(sig, callback)) { 137 return false; 138 } 139 } 140 return true; 141 } 142 143 IOEventRef IOEventLoop::AddPeriodicEvent(timeval duration, const std::function<bool()>& callback) { 144 return AddEvent(-1, EV_PERSIST, &duration, callback); 145 } 146 147 IOEventRef IOEventLoop::AddEvent(int fd_or_sig, int16_t events, timeval* timeout, 148 const std::function<bool()>& callback) { 149 if (!EnsureInit()) { 150 return nullptr; 151 } 152 std::unique_ptr<IOEvent> e(new IOEvent(this, callback)); 153 e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get()); 154 if (e->e == nullptr) { 155 LOG(ERROR) << "event_new() failed"; 156 return nullptr; 157 } 158 if (event_add(e->e, timeout) != 0) { 159 LOG(ERROR) << "event_add() failed"; 160 return nullptr; 161 } 162 if (timeout != nullptr) { 163 e->timeout = *timeout; 164 } 165 e->enabled = true; 166 events_.push_back(std::move(e)); 167 return events_.back().get(); 168 } 169 170 bool IOEventLoop::RunLoop() { 171 in_loop_ = true; 172 if (event_base_dispatch(ebase_) == -1) { 173 LOG(ERROR) << "event_base_dispatch() failed"; 174 in_loop_ = false; 175 return false; 176 } 177 if (has_error_) { 178 return false; 179 } 180 return true; 181 } 182 183 bool IOEventLoop::ExitLoop() { 184 if (in_loop_) { 185 if (event_base_loopbreak(ebase_) == -1) { 186 LOG(ERROR) << "event_base_loopbreak() failed"; 187 return false; 188 } 189 in_loop_ = false; 190 } 191 return true; 192 } 193 194 bool IOEventLoop::DisableEvent(IOEventRef ref) { 195 if (ref->enabled) { 196 if (event_del(ref->e) != 0) { 197 LOG(ERROR) << "event_del() failed"; 198 return false; 199 } 200 ref->enabled = false; 201 } 202 return true; 203 } 204 205 bool IOEventLoop::EnableEvent(IOEventRef ref) { 206 if (!ref->enabled) { 207 timeval* timeout = (ref->timeout.tv_sec != 0 || ref->timeout.tv_usec != 0) ? 208 &ref->timeout : nullptr; 209 if (event_add(ref->e, timeout) != 0) { 210 LOG(ERROR) << "event_add() failed"; 211 return false; 212 } 213 ref->enabled = true; 214 } 215 return true; 216 } 217 218 bool IOEventLoop::DelEvent(IOEventRef ref) { 219 DisableEvent(ref); 220 IOEventLoop* loop = ref->loop; 221 for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) { 222 if (it->get() == ref) { 223 loop->events_.erase(it); 224 break; 225 } 226 } 227 return true; 228 } 229