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