12251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include "epoll_event_dispatcher.h"
22251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
32251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include <log/log.h>
42251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include <sys/epoll.h>
52251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include <sys/eventfd.h>
62251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include <sys/prctl.h>
72251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
82251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka#include <dvr/performance_client_api.h>
92251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
102251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakanamespace android {
112251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakanamespace dvr {
122251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
132251d822dac2a96aad4184a6fdc2690f0a58af7cCorey TabakaEpollEventDispatcher::EpollEventDispatcher() {
142251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  epoll_fd_.Reset(epoll_create(64));
152251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  if (!epoll_fd_) {
162251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGE("Failed to create epoll fd: %s", strerror(errno));
172251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return;
182251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
192251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
202251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
212251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  if (!event_fd_) {
222251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGE("Failed to create event for epolling: %s", strerror(errno));
232251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return;
242251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
252251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
262251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
272251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  // when eventfd_write occurs. Use "this" as a unique sentinal value to
282251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  // identify events from the event fd.
292251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
302251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
312251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
322251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return;
332251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
342251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
352251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
362251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}
372251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
382251d822dac2a96aad4184a6fdc2690f0a58af7cCorey TabakaEpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
392251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
402251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakavoid EpollEventDispatcher::Stop() {
412251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  exit_thread_.store(true);
422251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  eventfd_write(event_fd_.Get(), 1);
432251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}
442251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
452251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakapdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
462251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka                                                        Handler handler) {
472251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  std::lock_guard<std::mutex> lock(lock_);
482251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
492251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  epoll_event event;
502251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  event.events = event_mask;
512251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  event.data.ptr = &(handlers_[fd] = handler);
522251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
532251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  ALOGD_IF(
542251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      TRACE,
552251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
562251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      fd, event_mask, event.data.ptr);
572251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
582251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
592251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    const int error = errno;
602251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
612251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return pdx::ErrorStatus(error);
622251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  } else {
632251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return {};
642251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
652251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}
662251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
672251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakapdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
682251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
692251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  std::lock_guard<std::mutex> lock(lock_);
702251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
712251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  epoll_event dummy;  // See BUGS in man 2 epoll_ctl.
722251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) {
732251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    const int error = errno;
742251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
752251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    return pdx::ErrorStatus(error);
762251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
772251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
782251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  // If the fd was valid above, add it to the list of ids to remove.
792251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  removed_handlers_.push_back(fd);
802251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
812251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  // Wake up the event thread to clean up.
822251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  eventfd_write(event_fd_.Get(), 1);
832251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
842251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  return {};
852251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}
862251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
872251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabakavoid EpollEventDispatcher::EventThread() {
882251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
892251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
902251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  const int error = dvrSetSchedulerClass(0, "graphics");
912251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  LOG_ALWAYS_FATAL_IF(
922251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      error < 0,
932251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
942251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      strerror(-error));
952251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
962251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  const size_t kMaxNumEvents = 128;
972251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  epoll_event events[kMaxNumEvents];
982251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
992251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  while (!exit_thread_.load()) {
1002251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
1012251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    if (num_events < 0 && errno != EINTR)
1022251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      break;
1032251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
1042251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    ALOGD_IF(TRACE, "EpollEventDispatcher::EventThread: num_events=%d",
1052251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka             num_events);
1062251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
1072251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    for (int i = 0; i < num_events; i++) {
1082251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      ALOGD_IF(
1092251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka          TRACE,
1102251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka          "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
1112251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka          i, events[i].data.ptr, events[i].events);
1122251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
1132251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      if (events[i].data.ptr == this) {
1142251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        // Clear pending event on event_fd_. Serialize the read with respect to
1152251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        // writes from other threads.
1162251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        std::lock_guard<std::mutex> lock(lock_);
1172251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        eventfd_t value;
1182251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        eventfd_read(event_fd_.Get(), &value);
1192251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      } else {
1202251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
1212251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka        if (handler)
1222251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka          (*handler)(events[i].events);
1232251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      }
1242251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    }
1252251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
1262251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    // Remove any handlers that have been posted for removal. This is done here
1272251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    // instead of in RemoveEventHandler() to prevent races between the dispatch
1282251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    // thread and the code requesting the removal. Handlers are guaranteed to
1292251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    // stay alive between exiting epoll_wait() and the dispatch loop above.
1302251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    std::lock_guard<std::mutex> lock(lock_);
1312251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    for (auto handler_fd : removed_handlers_) {
1322251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      ALOGD_IF(TRACE,
1332251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka               "EpollEventDispatcher::EventThread: removing handler: fd=%d",
1342251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka               handler_fd);
1352251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka      handlers_.erase(handler_fd);
1362251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    }
1372251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka    removed_handlers_.clear();
1382251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka  }
1392251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}
1402251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka
1412251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}  // namespace dvr
1422251d822dac2a96aad4184a6fdc2690f0a58af7cCorey Tabaka}  // namespace android
143