| /* |
| * 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() { |
| 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; |
| } |