summaryrefslogtreecommitdiff
path: root/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
blob: 0d5eb8080f6cbafd34e07af1f653d4e210ddf87b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "epoll_event_dispatcher.h"

#include <log/log.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/prctl.h>

#include <dvr/performance_client_api.h>

namespace android {
namespace dvr {

EpollEventDispatcher::EpollEventDispatcher() {
  epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
  if (!epoll_fd_) {
    ALOGE("Failed to create epoll fd: %s", strerror(errno));
    return;
  }

  event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
  if (!event_fd_) {
    ALOGE("Failed to create event for epolling: %s", strerror(errno));
    return;
  }

  // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
  // when eventfd_write occurs. Use "this" as a unique sentinal value to
  // identify events from the event fd.
  epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
    ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
    return;
  }

  thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
}

EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }

void EpollEventDispatcher::Stop() {
  exit_thread_.store(true);
  eventfd_write(event_fd_.Get(), 1);
}

pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
                                                        Handler handler) {
  std::lock_guard<std::mutex> lock(lock_);

  epoll_event event;
  event.events = event_mask;
  event.data.ptr = &(handlers_[fd] = handler);

  ALOGD_IF(
      TRACE,
      "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
      fd, event_mask, event.data.ptr);

  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
    const int error = errno;
    ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
    return pdx::ErrorStatus(error);
  } else {
    return {};
  }
}

pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
  ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
  std::lock_guard<std::mutex> lock(lock_);

  epoll_event ee;  // See BUGS in man 2 epoll_ctl.
  if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
    const int error = errno;
    ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
    return pdx::ErrorStatus(error);
  }

  // If the fd was valid above, add it to the list of ids to remove.
  removed_handlers_.push_back(fd);

  // Wake up the event thread to clean up.
  eventfd_write(event_fd_.Get(), 1);

  return {};
}

void EpollEventDispatcher::EventThread() {
  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);

  const int error = dvrSetSchedulerClass(0, "graphics");
  LOG_ALWAYS_FATAL_IF(
      error < 0,
      "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
      strerror(-error));

  const size_t kMaxNumEvents = 128;
  epoll_event events[kMaxNumEvents];

  while (!exit_thread_.load()) {
    const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
    if (num_events < 0 && errno != EINTR)
      break;

    ALOGD_IF(TRACE > 1, "EpollEventDispatcher::EventThread: num_events=%d",
             num_events);

    for (int i = 0; i < num_events; i++) {
      ALOGD_IF(
          TRACE > 1,
          "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
          i, events[i].data.ptr, events[i].events);

      if (events[i].data.ptr == this) {
        // Clear pending event on event_fd_. Serialize the read with respect to
        // writes from other threads.
        std::lock_guard<std::mutex> lock(lock_);
        eventfd_t value;
        eventfd_read(event_fd_.Get(), &value);
      } else {
        auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
        if (handler)
          (*handler)(events[i].events);
      }
    }

    // Remove any handlers that have been posted for removal. This is done here
    // instead of in RemoveEventHandler() to prevent races between the dispatch
    // thread and the code requesting the removal. Handlers are guaranteed to
    // stay alive between exiting epoll_wait() and the dispatch loop above.
    std::lock_guard<std::mutex> lock(lock_);
    for (auto handler_fd : removed_handlers_) {
      ALOGD_IF(TRACE,
               "EpollEventDispatcher::EventThread: removing handler: fd=%d",
               handler_fd);
      handlers_.erase(handler_fd);
    }
    removed_handlers_.clear();
  }
}

}  // namespace dvr
}  // namespace android