Corey Tabaka | 2251d82 | 2017-04-20 16:04:07 -0700 | [diff] [blame] | 1 | #include "epoll_event_dispatcher.h" |
| 2 | |
| 3 | #include <log/log.h> |
| 4 | #include <sys/epoll.h> |
| 5 | #include <sys/eventfd.h> |
| 6 | #include <sys/prctl.h> |
| 7 | |
| 8 | #include <dvr/performance_client_api.h> |
| 9 | |
| 10 | namespace android { |
| 11 | namespace dvr { |
| 12 | |
| 13 | EpollEventDispatcher::EpollEventDispatcher() { |
Nick Kralevich | fcf1b2b | 2018-12-15 11:59:30 -0800 | [diff] [blame] | 14 | epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); |
Corey Tabaka | 2251d82 | 2017-04-20 16:04:07 -0700 | [diff] [blame] | 15 | if (!epoll_fd_) { |
| 16 | ALOGE("Failed to create epoll fd: %s", strerror(errno)); |
| 17 | return; |
| 18 | } |
| 19 | |
| 20 | event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); |
| 21 | if (!event_fd_) { |
| 22 | ALOGE("Failed to create event for epolling: %s", strerror(errno)); |
| 23 | return; |
| 24 | } |
| 25 | |
| 26 | // Add watch for eventfd. This should only watch for EPOLLIN, which gets set |
| 27 | // when eventfd_write occurs. Use "this" as a unique sentinal value to |
| 28 | // identify events from the event fd. |
| 29 | epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}}; |
| 30 | if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) { |
| 31 | ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno)); |
| 32 | return; |
| 33 | } |
| 34 | |
| 35 | thread_ = std::thread(&EpollEventDispatcher::EventThread, this); |
| 36 | } |
| 37 | |
| 38 | EpollEventDispatcher::~EpollEventDispatcher() { Stop(); } |
| 39 | |
| 40 | void EpollEventDispatcher::Stop() { |
| 41 | exit_thread_.store(true); |
| 42 | eventfd_write(event_fd_.Get(), 1); |
| 43 | } |
| 44 | |
| 45 | pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask, |
| 46 | Handler handler) { |
| 47 | std::lock_guard<std::mutex> lock(lock_); |
| 48 | |
| 49 | epoll_event event; |
| 50 | event.events = event_mask; |
| 51 | event.data.ptr = &(handlers_[fd] = handler); |
| 52 | |
| 53 | ALOGD_IF( |
| 54 | TRACE, |
| 55 | "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p", |
| 56 | fd, event_mask, event.data.ptr); |
| 57 | |
| 58 | if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) { |
| 59 | const int error = errno; |
| 60 | ALOGE("Failed to add fd to epoll set because: %s", strerror(error)); |
| 61 | return pdx::ErrorStatus(error); |
| 62 | } else { |
| 63 | return {}; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) { |
| 68 | ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd); |
| 69 | std::lock_guard<std::mutex> lock(lock_); |
| 70 | |
Peiyong Lin | d8460c8 | 2020-07-28 16:04:22 -0700 | [diff] [blame] | 71 | epoll_event ee; // See BUGS in man 2 epoll_ctl. |
| 72 | if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) { |
Corey Tabaka | 2251d82 | 2017-04-20 16:04:07 -0700 | [diff] [blame] | 73 | const int error = errno; |
| 74 | ALOGE("Failed to remove fd from epoll set because: %s", strerror(error)); |
| 75 | return pdx::ErrorStatus(error); |
| 76 | } |
| 77 | |
| 78 | // If the fd was valid above, add it to the list of ids to remove. |
| 79 | removed_handlers_.push_back(fd); |
| 80 | |
| 81 | // Wake up the event thread to clean up. |
| 82 | eventfd_write(event_fd_.Get(), 1); |
| 83 | |
| 84 | return {}; |
| 85 | } |
| 86 | |
| 87 | void EpollEventDispatcher::EventThread() { |
| 88 | prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0); |
| 89 | |
| 90 | const int error = dvrSetSchedulerClass(0, "graphics"); |
| 91 | LOG_ALWAYS_FATAL_IF( |
| 92 | error < 0, |
| 93 | "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s", |
| 94 | strerror(-error)); |
| 95 | |
| 96 | const size_t kMaxNumEvents = 128; |
| 97 | epoll_event events[kMaxNumEvents]; |
| 98 | |
| 99 | while (!exit_thread_.load()) { |
| 100 | const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1); |
| 101 | if (num_events < 0 && errno != EINTR) |
| 102 | break; |
| 103 | |
Corey Tabaka | 89bbefc | 2017-06-06 16:14:21 -0700 | [diff] [blame] | 104 | ALOGD_IF(TRACE > 1, "EpollEventDispatcher::EventThread: num_events=%d", |
Corey Tabaka | 2251d82 | 2017-04-20 16:04:07 -0700 | [diff] [blame] | 105 | num_events); |
| 106 | |
| 107 | for (int i = 0; i < num_events; i++) { |
| 108 | ALOGD_IF( |
Corey Tabaka | 89bbefc | 2017-06-06 16:14:21 -0700 | [diff] [blame] | 109 | TRACE > 1, |
Corey Tabaka | 2251d82 | 2017-04-20 16:04:07 -0700 | [diff] [blame] | 110 | "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x", |
| 111 | i, events[i].data.ptr, events[i].events); |
| 112 | |
| 113 | if (events[i].data.ptr == this) { |
| 114 | // Clear pending event on event_fd_. Serialize the read with respect to |
| 115 | // writes from other threads. |
| 116 | std::lock_guard<std::mutex> lock(lock_); |
| 117 | eventfd_t value; |
| 118 | eventfd_read(event_fd_.Get(), &value); |
| 119 | } else { |
| 120 | auto handler = reinterpret_cast<Handler*>(events[i].data.ptr); |
| 121 | if (handler) |
| 122 | (*handler)(events[i].events); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | // Remove any handlers that have been posted for removal. This is done here |
| 127 | // instead of in RemoveEventHandler() to prevent races between the dispatch |
| 128 | // thread and the code requesting the removal. Handlers are guaranteed to |
| 129 | // stay alive between exiting epoll_wait() and the dispatch loop above. |
| 130 | std::lock_guard<std::mutex> lock(lock_); |
| 131 | for (auto handler_fd : removed_handlers_) { |
| 132 | ALOGD_IF(TRACE, |
| 133 | "EpollEventDispatcher::EventThread: removing handler: fd=%d", |
| 134 | handler_fd); |
| 135 | handlers_.erase(handler_fd); |
| 136 | } |
| 137 | removed_handlers_.clear(); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | } // namespace dvr |
| 142 | } // namespace android |