base: extract Event class for eventfd/pipe

Extracts a common class to wrap eventfd and its pipe-based
fallback for Mac. This is going to be used for the iorap work
and hence hit the "three strikes and you refactor" rule.

Bug: 116547201
Change-Id: I1d6f2bb5aaab32f2dd4e3d888b3409e8f8df276c
diff --git a/Android.bp b/Android.bp
index 541e66f..480e618 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,6 +32,7 @@
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
@@ -157,6 +158,7 @@
     ":perfetto_src_ipc_wire_protocol_gen",
     ":perfetto_src_perfetto_cmd_protos_gen",
     "src/base/android_task_runner.cc",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
@@ -297,6 +299,7 @@
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
     "src/base/android_task_runner.cc",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
@@ -3612,6 +3615,7 @@
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
@@ -3800,6 +3804,7 @@
     ":perfetto_src_traced_probes_ftrace_test_messages_lite_gen",
     ":perfetto_src_traced_probes_ftrace_test_messages_zero_gen",
     "src/base/android_task_runner.cc",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
@@ -4033,6 +4038,7 @@
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    "src/base/event.cc",
     "src/base/file_utils.cc",
     "src/base/metatrace.cc",
     "src/base/page_allocator.cc",
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index 10da3d0..5ee76ba 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -17,6 +17,7 @@
 source_set("base") {
   sources = [
     "build_config.h",
+    "event.h",
     "export.h",
     "file_utils.h",
     "logging.h",
diff --git a/include/perfetto/base/android_task_runner.h b/include/perfetto/base/android_task_runner.h
index 5087330..5aebbac 100644
--- a/include/perfetto/base/android_task_runner.h
+++ b/include/perfetto/base/android_task_runner.h
@@ -17,6 +17,7 @@
 #ifndef INCLUDE_PERFETTO_BASE_ANDROID_TASK_RUNNER_H_
 #define INCLUDE_PERFETTO_BASE_ANDROID_TASK_RUNNER_H_
 
+#include "perfetto/base/event.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/thread_checker.h"
@@ -69,7 +70,7 @@
   void ScheduleDelayedWakeUp(TimeMillis time);
 
   ALooper* const looper_;
-  ScopedFile immediate_event_;
+  Event immediate_event_;
   ScopedFile delayed_timer_;
 
   ThreadChecker thread_checker_;
diff --git a/include/perfetto/base/event.h b/include/perfetto/base/event.h
new file mode 100644
index 0000000..bca6c00
--- /dev/null
+++ b/include/perfetto/base/event.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef INCLUDE_PERFETTO_BASE_EVENT_H_
+#define INCLUDE_PERFETTO_BASE_EVENT_H_
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/base/scoped_file.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#define PERFETTO_USE_EVENTFD() 1
+#else
+#define PERFETTO_USE_EVENTFD() 0
+#endif
+
+namespace perfetto {
+namespace base {
+
+// A waitable event that can be used with poll/select.
+// This is really a wrapper around eventfd_create with a pipe-based fallback
+// for other platforms where eventfd is not supported.
+class Event {
+ public:
+  Event();
+  ~Event();
+  Event(Event&&) noexcept = default;
+  Event& operator=(Event&&) = default;
+
+  // The non-blocking file descriptor that can be polled to wait for the event.
+  int fd() const { return fd_.get(); }
+
+  // Can be called from any thread.
+  void Notify();
+
+  // Can be called from any thread. If more Notify() are queued a Clear() call
+  // can clear all of them (up to 16 per call).
+  void Clear();
+
+ private:
+  // The eventfd, when eventfd is supported, otherwise this is the read end of
+  // the pipe for fallback mode.
+  ScopedFile fd_;
+
+#if !PERFETTO_USE_EVENTFD()
+  // The write end of the wakeup pipe.
+  ScopedFile write_fd_;
+#endif
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_BASE_EVENT_H_
diff --git a/include/perfetto/base/unix_task_runner.h b/include/perfetto/base/unix_task_runner.h
index 65c8361..3d1a896 100644
--- a/include/perfetto/base/unix_task_runner.h
+++ b/include/perfetto/base/unix_task_runner.h
@@ -18,6 +18,7 @@
 #define INCLUDE_PERFETTO_BASE_UNIX_TASK_RUNNER_H_
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/event.h"
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/thread_checker.h"
@@ -30,13 +31,6 @@
 #include <mutex>
 #include <vector>
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-#define PERFETTO_USE_EVENTFD() 1
-#else
-#define PERFETTO_USE_EVENTFD() 0
-#endif
-
 namespace perfetto {
 namespace base {
 
@@ -75,11 +69,7 @@
 
   // On Linux, an eventfd(2) used to waking up the task runner when a new task
   // is posted. Otherwise the read end of a pipe used for the same purpose.
-  ScopedFile event_;
-#if !PERFETTO_USE_EVENTFD()
-  // The write end of the wakeup pipe.
-  ScopedFile event_write_;
-#endif
+  Event event_;
 
   std::vector<struct pollfd> poll_fds_;
 
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 019cd7b..ad3f170 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -37,6 +37,7 @@
   # TODO(brucedawson): Enable these for Windows when possible.
   if (!is_win) {
     sources += [
+      "event.cc",
       "temp_file.cc",
       "unix_task_runner.cc",
     ]
diff --git a/src/base/android_task_runner.cc b/src/base/android_task_runner.cc
index d8e7cc7..23697c8 100644
--- a/src/base/android_task_runner.cc
+++ b/src/base/android_task_runner.cc
@@ -17,7 +17,6 @@
 #include "perfetto/base/android_task_runner.h"
 
 #include <errno.h>
-#include <sys/eventfd.h>
 #include <sys/timerfd.h>
 
 namespace perfetto {
@@ -25,13 +24,11 @@
 
 AndroidTaskRunner::AndroidTaskRunner()
     : looper_(ALooper_prepare(0 /* require callbacks */)),
-      immediate_event_(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)),
       delayed_timer_(
           timerfd_create(kWallTimeClockSource, TFD_NONBLOCK | TFD_CLOEXEC)) {
   ALooper_acquire(looper_);
-  PERFETTO_CHECK(immediate_event_);
   PERFETTO_CHECK(delayed_timer_);
-  AddFileDescriptorWatch(immediate_event_.get(),
+  AddFileDescriptorWatch(immediate_event_.fd(),
                          std::bind(&AndroidTaskRunner::RunImmediateTask, this));
   AddFileDescriptorWatch(delayed_timer_.get(),
                          std::bind(&AndroidTaskRunner::RunDelayedTask, this));
@@ -81,11 +78,7 @@
 }
 
 void AndroidTaskRunner::RunImmediateTask() {
-  uint64_t unused = 0;
-  if (read(immediate_event_.get(), &unused, sizeof(unused)) != sizeof(unused) &&
-      errno != EAGAIN) {
-    PERFETTO_DPLOG("read");
-  }
+  immediate_event_.Clear();
 
   // If locking overhead becomes an issue, add a separate work queue.
   bool has_next;
@@ -133,11 +126,7 @@
 }
 
 void AndroidTaskRunner::ScheduleImmediateWakeUp() {
-  uint64_t value = 1;
-  if (write(immediate_event_.get(), &value, sizeof(value)) == -1 &&
-      errno != EAGAIN) {
-    PERFETTO_DPLOG("write");
-  }
+  immediate_event_.Notify();
 }
 
 void AndroidTaskRunner::ScheduleDelayedWakeUp(TimeMillis time) {
diff --git a/src/base/event.cc b/src/base/event.cc
new file mode 100644
index 0000000..4085618
--- /dev/null
+++ b/src/base/event.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+#include <unistd.h>
+
+#include "perfetto/base/event.h"
+#include "perfetto/base/logging.h"
+
+#if PERFETTO_USE_EVENTFD()
+#include <sys/eventfd.h>
+#endif
+
+namespace perfetto {
+namespace base {
+
+Event::Event() {
+#if PERFETTO_USE_EVENTFD()
+  fd_.reset(eventfd(/* start value */ 0, EFD_CLOEXEC | EFD_NONBLOCK));
+  PERFETTO_CHECK(fd_);
+#else
+  int pipe_fds[2];
+  PERFETTO_CHECK(pipe(pipe_fds) == 0);
+
+  // Make the pipe non-blocking so that we never block the waking thread (either
+  // the main thread or another one) when scheduling a wake-up.
+  for (auto fd : pipe_fds) {
+    int flags = fcntl(fd, F_GETFL, 0);
+    PERFETTO_CHECK(flags != -1);
+    PERFETTO_CHECK(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
+    PERFETTO_CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
+  }
+  fd_.reset(pipe_fds[0]);
+  write_fd_.reset(pipe_fds[1]);
+#endif  // !PERFETTO_USE_EVENTFD()
+}
+
+Event::~Event() = default;
+
+void Event::Notify() {
+  const uint64_t value = 1;
+
+#if PERFETTO_USE_EVENTFD()
+  ssize_t ret = write(fd_.get(), &value, sizeof(value));
+#else
+  ssize_t ret = write(write_fd_.get(), &value, sizeof(uint8_t));
+#endif
+
+  if (ret <= 0 && errno != EAGAIN) {
+    PERFETTO_DPLOG("write()");
+    PERFETTO_DCHECK(false);
+  }
+}
+
+void Event::Clear() {
+#if PERFETTO_USE_EVENTFD()
+  uint64_t value;
+  ssize_t ret = read(fd_.get(), &value, sizeof(value));
+#else
+  // Drain the byte(s) written to the wake-up pipe. We can potentially read
+  // more than one byte if several wake-ups have been scheduled.
+  char buffer[16];
+  ssize_t ret = read(fd_.get(), &buffer[0], sizeof(buffer));
+#endif
+  if (ret <= 0 && errno != EAGAIN)
+    PERFETTO_DPLOG("read()");
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/unix_task_runner.cc b/src/base/unix_task_runner.cc
index 006b7af..44501df 100644
--- a/src/base/unix_task_runner.cc
+++ b/src/base/unix_task_runner.cc
@@ -24,32 +24,11 @@
 
 #include <limits>
 
-#if PERFETTO_USE_EVENTFD()
-#include <sys/eventfd.h>
-#endif
-
 namespace perfetto {
 namespace base {
 
 UnixTaskRunner::UnixTaskRunner() {
-#if PERFETTO_USE_EVENTFD()
-  event_.reset(eventfd(/* start value */ 0, EFD_CLOEXEC | EFD_NONBLOCK));
-#else
-  int pipe_fds[2];
-  PERFETTO_CHECK(pipe(pipe_fds) == 0);
-
-  // Make the pipe non-blocking so that we never block the waking thread (either
-  // the main thread or another one) when scheduling a wake-up.
-  for (auto fd : pipe_fds) {
-    int flags = fcntl(fd, F_GETFL, 0);
-    PERFETTO_CHECK(flags != -1);
-    PERFETTO_CHECK(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
-    PERFETTO_CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0);
-  }
-  event_.reset(pipe_fds[0]);
-  event_write_.reset(pipe_fds[1]);
-#endif  // !PERFETTO_USE_EVENTFD()
-  AddFileDescriptorWatch(event_.get(), [] {
+  AddFileDescriptorWatch(event_.fd(), [] {
     // Not reached -- see PostFileDescriptorWatches().
     PERFETTO_DCHECK(false);
   });
@@ -58,14 +37,7 @@
 UnixTaskRunner::~UnixTaskRunner() = default;
 
 void UnixTaskRunner::WakeUp() {
-  const uint64_t value = 1;
-#if PERFETTO_USE_EVENTFD()
-  ssize_t ret = write(event_.get(), &value, sizeof(value));
-#else
-  ssize_t ret = write(event_write_.get(), &value, sizeof(uint8_t));
-#endif
-  if (ret <= 0 && errno != EAGAIN)
-    PERFETTO_DPLOG("write()");
+  event_.Notify();
 }
 
 void UnixTaskRunner::Run() {
@@ -153,18 +125,8 @@
 
     // The wake-up event is handled inline to avoid an infinite recursion of
     // posted tasks.
-    if (poll_fds_[i].fd == event_.get()) {
-#if PERFETTO_USE_EVENTFD()
-      uint64_t value;
-      ssize_t ret = read(event_.get(), &value, sizeof(value));
-#else
-      // Drain the byte(s) written to the wake-up pipe. We can potentially read
-      // more than one byte if several wake-ups have been scheduled.
-      char buffer[16];
-      ssize_t ret = read(event_.get(), &buffer[0], sizeof(buffer));
-#endif
-      if (ret <= 0 && errno != EAGAIN)
-        PERFETTO_DPLOG("read()");
+    if (poll_fds_[i].fd == event_.fd()) {
+      event_.Clear();
       continue;
     }