[adb] Optimize fdevent machinery

- Use one fewer heap allocation per fdevent object
- Lazy-init the fdevent context

Bug: 151239696
Test: various adb commands on Win/Linux

Change-Id: Ic7de207b30495e618f187e097c0276ad42c34005
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index 562f587..fd55020 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -63,7 +63,10 @@
 
     int fd_num = fd.get();
 
-    fdevent* fde = new fdevent();
+    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fdevent{});
+    CHECK(inserted);
+
+    fdevent* fde = &it->second;
     fde->id = fdevent_id_++;
     fde->state = 0;
     fde->fd = std::move(fd);
@@ -76,10 +79,6 @@
         LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
     }
 
-    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
-    CHECK(inserted);
-    UNUSED(it);
-
     this->Register(fde);
     return fde;
 }
@@ -92,12 +91,12 @@
 
     this->Unregister(fde);
 
-    auto erased = this->installed_fdevents_.erase(fde->fd.get());
+    unique_fd fd = std::move(fde->fd);
+
+    auto erased = this->installed_fdevents_.erase(fd.get());
     CHECK_EQ(1UL, erased);
 
-    unique_fd result = std::move(fde->fd);
-    delete fde;
-    return result;
+    return fd;
 }
 
 void fdevent_context::Add(fdevent* fde, unsigned events) {
@@ -123,9 +122,9 @@
 
     for (const auto& [fd, fde] : this->installed_fdevents_) {
         UNUSED(fd);
-        auto timeout_opt = fde->timeout;
+        auto timeout_opt = fde.timeout;
         if (timeout_opt) {
-            auto deadline = fde->last_active + *timeout_opt;
+            auto deadline = fde.last_active + *timeout_opt;
             auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
             if (time_left < 0ms) {
                 time_left = 0ms;
@@ -194,11 +193,13 @@
 #endif
 }
 
-static auto& g_ambient_fdevent_context =
-        *new std::unique_ptr<fdevent_context>(fdevent_create_context());
+static auto& g_ambient_fdevent_context() {
+    static auto context = fdevent_create_context().release();
+    return context;
+}
 
 static fdevent_context* fdevent_get_ambient() {
-    return g_ambient_fdevent_context.get();
+    return g_ambient_fdevent_context();
 }
 
 fdevent* fdevent_create(int fd, fd_func func, void* arg) {
@@ -256,5 +257,6 @@
 }
 
 void fdevent_reset() {
-    g_ambient_fdevent_context = fdevent_create_context();
+    auto old = std::exchange(g_ambient_fdevent_context(), fdevent_create_context().release());
+    delete old;
 }
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 86814d7..9fc3b2c 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -52,6 +52,20 @@
     unsigned events;
 };
 
+struct fdevent final {
+    uint64_t id;
+
+    unique_fd fd;
+    int force_eof = 0;
+
+    uint16_t state = 0;
+    std::optional<std::chrono::milliseconds> timeout;
+    std::chrono::steady_clock::time_point last_active;
+
+    std::variant<fd_func, fd_func2> func;
+    void* arg = nullptr;
+};
+
 struct fdevent_context {
   public:
     virtual ~fdevent_context() = default;
@@ -113,7 +127,7 @@
     std::atomic<bool> terminate_loop_ = false;
 
   protected:
-    std::unordered_map<int, fdevent*> installed_fdevents_;
+    std::unordered_map<int, fdevent> installed_fdevents_;
 
   private:
     uint64_t fdevent_id_ = 0;
@@ -121,20 +135,6 @@
     std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
 };
 
-struct fdevent {
-    uint64_t id;
-
-    unique_fd fd;
-    int force_eof = 0;
-
-    uint16_t state = 0;
-    std::optional<std::chrono::milliseconds> timeout;
-    std::chrono::steady_clock::time_point last_active;
-
-    std::variant<fd_func, fd_func2> func;
-    void* arg = nullptr;
-};
-
 // Backwards compatibility shims that forward to the global fdevent_context.
 fdevent* fdevent_create(int fd, fd_func func, void* arg);
 fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
index e3d1674..4ef41d1 100644
--- a/adb/fdevent/fdevent_epoll.cpp
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -155,15 +155,15 @@
             event_map[fde] = events;
         }
 
-        for (const auto& [fd, fde] : installed_fdevents_) {
+        for (auto& [fd, fde] : installed_fdevents_) {
             unsigned events = 0;
-            if (auto it = event_map.find(fde); it != event_map.end()) {
+            if (auto it = event_map.find(&fde); it != event_map.end()) {
                 events = it->second;
             }
 
             if (events == 0) {
-                if (fde->timeout) {
-                    auto deadline = fde->last_active + *fde->timeout;
+                if (fde.timeout) {
+                    auto deadline = fde.last_active + *fde.timeout;
                     if (deadline < post_poll) {
                         events |= FDE_TIMEOUT;
                     }
@@ -171,13 +171,13 @@
             }
 
             if (events != 0) {
-                LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+                LOG(DEBUG) << dump_fde(&fde) << " got events " << std::hex << std::showbase
                            << events;
-                fde_events.push_back({fde, events});
-                fde->last_active = post_poll;
+                fde_events.push_back({&fde, events});
+                fde.last_active = post_poll;
             }
         }
-        this->HandleEvents(std::move(fde_events));
+        this->HandleEvents(fde_events);
         fde_events.clear();
     }
 
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
index 684fa32..6214d2e 100644
--- a/adb/fdevent/fdevent_epoll.h
+++ b/adb/fdevent/fdevent_epoll.h
@@ -47,12 +47,7 @@
   protected:
     virtual void Interrupt() final;
 
-  public:
-    // All operations to fdevent should happen only in the main thread.
-    // That's why we don't need a lock for fdevent.
-    std::unordered_map<int, fdevent*> epoll_node_map_;
-    std::list<fdevent*> pending_list_;
-
+  private:
     unique_fd epoll_fd_;
     unique_fd interrupt_fd_;
     fdevent* interrupt_fde_ = nullptr;
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index cc4a7a1..ac86c08 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -103,24 +103,27 @@
 void fdevent_context_poll::Loop() {
     main_thread_id_ = android::base::GetThreadId();
 
+    std::vector<adb_pollfd> pollfds;
+    std::vector<fdevent_event> poll_events;
+
     while (true) {
         if (terminate_loop_) {
             break;
         }
 
         D("--- --- waiting for events");
-        std::vector<adb_pollfd> pollfds;
+        pollfds.clear();
         for (const auto& [fd, fde] : this->installed_fdevents_) {
             adb_pollfd pfd;
             pfd.fd = fd;
             pfd.events = 0;
-            if (fde->state & FDE_READ) {
+            if (fde.state & FDE_READ) {
                 pfd.events |= POLLIN;
             }
-            if (fde->state & FDE_WRITE) {
+            if (fde.state & FDE_WRITE) {
                 pfd.events |= POLLOUT;
             }
-            if (fde->state & FDE_ERROR) {
+            if (fde.state & FDE_ERROR) {
                 pfd.events |= POLLERR;
             }
 #if defined(__linux__)
@@ -147,7 +150,6 @@
         }
 
         auto post_poll = std::chrono::steady_clock::now();
-        std::vector<fdevent_event> poll_events;
 
         for (const auto& pollfd : pollfds) {
             unsigned events = 0;
@@ -170,7 +172,7 @@
 
             auto it = this->installed_fdevents_.find(pollfd.fd);
             CHECK(it != this->installed_fdevents_.end());
-            fdevent* fde = it->second;
+            fdevent* fde = &it->second;
 
             if (events == 0) {
                 if (fde->timeout) {
@@ -187,7 +189,8 @@
                 fde->last_active = post_poll;
             }
         }
-        this->HandleEvents(std::move(poll_events));
+        this->HandleEvents(poll_events);
+        poll_events.clear();
     }
 
     main_thread_id_.reset();