/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "IOEventLoop.h" #include <event2/event.h> #include <fcntl.h> #include <android-base/logging.h> struct IOEvent { IOEventLoop* loop; event* e; std::function<bool()> callback; bool enabled; IOEvent(IOEventLoop* loop, const std::function<bool()>& callback) : loop(loop), e(nullptr), callback(callback), enabled(false) {} ~IOEvent() { if (e != nullptr) { event_free(e); } } }; IOEventLoop::IOEventLoop() : ebase_(nullptr), has_error_(false) {} IOEventLoop::~IOEventLoop() { events_.clear(); if (ebase_ != nullptr) { event_base_free(ebase_); } } bool IOEventLoop::EnsureInit() { if (ebase_ == nullptr) { ebase_ = event_base_new(); if (ebase_ == nullptr) { LOG(ERROR) << "failed to call event_base_new()"; return false; } } return true; } void IOEventLoop::EventCallbackFn(int, short, void* arg) { IOEvent* e = static_cast<IOEvent*>(arg); if (!e->callback()) { e->loop->has_error_ = true; e->loop->ExitLoop(); } } static bool MakeFdNonBlocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { PLOG(ERROR) << "fcntl() failed"; return false; } return true; } IOEventRef IOEventLoop::AddReadEvent(int fd, const std::function<bool()>& callback) { if (!MakeFdNonBlocking(fd)) { return nullptr; } return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback); } IOEventRef IOEventLoop::AddWriteEvent(int fd, const std::function<bool()>& callback) { if (!MakeFdNonBlocking(fd)) { return nullptr; } return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback); } bool IOEventLoop::AddSignalEvent(int sig, const std::function<bool()>& callback) { return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr; } bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback) { for (auto sig : sigs) { if (!AddSignalEvent(sig, callback)) { return false; } } return true; } bool IOEventLoop::AddPeriodicEvent(timeval duration, const std::function<bool()>& callback) { return AddEvent(-1, EV_PERSIST, &duration, callback) != nullptr; } IOEventRef IOEventLoop::AddEvent(int fd_or_sig, short events, timeval* timeout, const std::function<bool()>& callback) { if (!EnsureInit()) { return nullptr; } std::unique_ptr<IOEvent> e(new IOEvent(this, callback)); e->e = event_new(ebase_, fd_or_sig, events, EventCallbackFn, e.get()); if (e->e == nullptr) { LOG(ERROR) << "event_new() failed"; return nullptr; } if (event_add(e->e, timeout) != 0) { LOG(ERROR) << "event_add() failed"; return nullptr; } e->enabled = true; events_.push_back(std::move(e)); return events_.back().get(); } bool IOEventLoop::RunLoop() { if (event_base_dispatch(ebase_) == -1) { LOG(ERROR) << "event_base_dispatch() failed"; return false; } if (has_error_) { return false; } return true; } bool IOEventLoop::ExitLoop() { if (event_base_loopbreak(ebase_) == -1) { LOG(ERROR) << "event_base_loopbreak() failed"; return false; } return true; } bool IOEventLoop::DisableEvent(IOEventRef ref) { if (ref->enabled) { if (event_del(ref->e) != 0) { LOG(ERROR) << "event_del() failed"; return false; } ref->enabled = false; } return true; } bool IOEventLoop::EnableEvent(IOEventRef ref) { if (!ref->enabled) { if (event_add(ref->e, nullptr) != 0) { LOG(ERROR) << "event_add() failed"; return false; } ref->enabled = true; } return true; } bool IOEventLoop::DelEvent(IOEventRef ref) { DisableEvent(ref); IOEventLoop* loop = ref->loop; for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) { if (it->get() == ref) { loop->events_.erase(it); break; } } return true; }