IOEventLoop.cpp revision a383c11bc5d56a3d245f5a915d3a8122db46b833
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 24struct IOEvent { 25 IOEventLoop* loop; 26 event* e; 27 std::function<bool()> callback; 28 bool enabled; 29 30 IOEvent(IOEventLoop* loop, const std::function<bool()>& callback) 31 : loop(loop), e(nullptr), callback(callback), enabled(false) {} 32 33 ~IOEvent() { 34 if (e != nullptr) { 35 event_free(e); 36 } 37 } 38}; 39 40IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {} 41 42IOEventLoop::~IOEventLoop() { 43 if (ebase_ != nullptr) { 44 event_base_free(ebase_); 45 } 46} 47 48bool IOEventLoop::EnsureInit() { 49 if (ebase_ == nullptr) { 50 ebase_ = event_base_new(); 51 if (ebase_ == nullptr) { 52 LOG(ERROR) << "failed to call event_base_new()"; 53 return false; 54 } 55 } 56 return true; 57} 58 59void IOEventLoop::EventCallbackFn(int, short, void* arg) { 60 IOEvent* e = static_cast<IOEvent*>(arg); 61 if (!e->callback()) { 62 e->loop->has_error_ = true; 63 e->loop->ExitLoop(); 64 } 65} 66 67static bool MakeFdNonBlocking(int fd) { 68 int flags = fcntl(fd, F_GETFL, 0); 69 if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { 70 PLOG(ERROR) << "fcntl() failed"; 71 return false; 72 } 73 return true; 74} 75 76IOEventRef IOEventLoop::AddReadEvent(int fd, 77 const std::function<bool()>& callback) { 78 if (!MakeFdNonBlocking(fd)) { 79 return nullptr; 80 } 81 return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback); 82} 83 84IOEventRef IOEventLoop::AddWriteEvent(int fd, 85 const std::function<bool()>& callback) { 86 if (!MakeFdNonBlocking(fd)) { 87 return nullptr; 88 } 89 return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback); 90} 91 92bool IOEventLoop::AddSignalEvent(int sig, 93 const std::function<bool()>& callback) { 94 return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr; 95} 96 97bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, 98 const std::function<bool()>& callback) { 99 for (auto sig : sigs) { 100 if (!AddSignalEvent(sig, callback)) { 101 return false; 102 } 103 } 104 return true; 105} 106 107bool IOEventLoop::AddPeriodicEvent(timeval duration, 108 const std::function<bool()>& callback) { 109 return AddEvent(-1, EV_PERSIST, &duration, callback) != nullptr; 110} 111 112IOEventRef IOEventLoop::AddEvent(int fd_or_sig, short events, timeval* timeout, 113 const std::function<bool()>& callback) { 114 if (!EnsureInit()) { 115 return nullptr; 116 } 117 std::unique_ptr<IOEvent> e(new IOEvent(this, callback)); 118 e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get()); 119 if (e->e == nullptr) { 120 LOG(ERROR) << "event_new() failed"; 121 return nullptr; 122 } 123 if (event_add(e->e, timeout) != 0) { 124 LOG(ERROR) << "event_add() failed"; 125 return nullptr; 126 } 127 e->enabled = true; 128 events_.push_back(std::move(e)); 129 return events_.back().get(); 130} 131 132bool IOEventLoop::RunLoop() { 133 if (event_base_dispatch(ebase_) == -1) { 134 LOG(ERROR) << "event_base_dispatch() failed"; 135 return false; 136 } 137 if (has_error_) { 138 return false; 139 } 140 return true; 141} 142 143bool IOEventLoop::ExitLoop() { 144 if (event_base_loopbreak(ebase_) == -1) { 145 LOG(ERROR) << "event_base_loopbreak() failed"; 146 return false; 147 } 148 return true; 149} 150 151bool IOEventLoop::DisableEvent(IOEventRef ref) { 152 if (ref->enabled) { 153 if (event_del(ref->e) != 0) { 154 LOG(ERROR) << "event_del() failed"; 155 return false; 156 } 157 ref->enabled = false; 158 } 159 return true; 160} 161 162bool IOEventLoop::EnableEvent(IOEventRef ref) { 163 if (!ref->enabled) { 164 if (event_add(ref->e, nullptr) != 0) { 165 LOG(ERROR) << "event_add() failed"; 166 return false; 167 } 168 ref->enabled = true; 169 } 170 return true; 171} 172 173bool IOEventLoop::DelEvent(IOEventRef ref) { 174 DisableEvent(ref); 175 IOEventLoop* loop = ref->loop; 176 for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) { 177 if (it->get() == ref) { 178 loop->events_.erase(it); 179 break; 180 } 181 } 182 return true; 183} 184