adb: implement fdevent_set_timeout.
Test: adb_test
Change-Id: I9ae61465617b6a2fe0a1c161ad88c4feae49ec80
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index fa3738d..32f9086 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -32,6 +32,7 @@
#include <functional>
#include <list>
#include <mutex>
+#include <optional>
#include <unordered_map>
#include <utility>
#include <variant>
@@ -225,14 +226,22 @@
void fdevent_add(fdevent* fde, unsigned events) {
check_main_thread();
+ CHECK(!(events & FDE_TIMEOUT));
fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
void fdevent_del(fdevent* fde, unsigned events) {
check_main_thread();
+ CHECK(!(events & FDE_TIMEOUT));
fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+ check_main_thread();
+ fde->timeout = timeout;
+ fde->last_active = std::chrono::steady_clock::now();
+}
+
static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
std::string result;
for (const auto& pollfd : pollfds) {
@@ -248,6 +257,32 @@
return result;
}
+static std::optional<std::chrono::milliseconds> calculate_timeout() {
+ std::optional<std::chrono::milliseconds> result = std::nullopt;
+ auto now = std::chrono::steady_clock::now();
+ check_main_thread();
+
+ for (const auto& [fd, pollnode] : g_poll_node_map) {
+ UNUSED(fd);
+ auto timeout_opt = pollnode.fde->timeout;
+ if (timeout_opt) {
+ auto deadline = pollnode.fde->last_active + *timeout_opt;
+ auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
+ if (time_left < std::chrono::milliseconds::zero()) {
+ time_left = std::chrono::milliseconds::zero();
+ }
+
+ if (!result) {
+ result = time_left;
+ } else {
+ result = std::min(*result, time_left);
+ }
+ }
+ }
+
+ return result;
+}
+
static void fdevent_process() {
std::vector<adb_pollfd> pollfds;
for (const auto& pair : g_poll_node_map) {
@@ -256,11 +291,22 @@
CHECK_GT(pollfds.size(), 0u);
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
- int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
+ auto timeout = calculate_timeout();
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
+ }
+
+ int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
if (ret == -1) {
PLOG(ERROR) << "poll(), ret = " << ret;
return;
}
+
+ auto post_poll = std::chrono::steady_clock::now();
+
for (const auto& pollfd : pollfds) {
if (pollfd.revents != 0) {
D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
@@ -282,12 +328,24 @@
events |= FDE_READ | FDE_ERROR;
}
#endif
+ auto it = g_poll_node_map.find(pollfd.fd);
+ CHECK(it != g_poll_node_map.end());
+ fdevent* fde = it->second.fde;
+
+ if (events == 0) {
+ // Check for timeout.
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
if (events != 0) {
- auto it = g_poll_node_map.find(pollfd.fd);
- CHECK(it != g_poll_node_map.end());
- fdevent* fde = it->second.fde;
CHECK_EQ(fde->fd.get(), pollfd.fd);
fde->events |= events;
+ fde->last_active = post_poll;
D("%s got events %x", dump_fde(fde).c_str(), events);
fde->state |= FDE_PENDING;
g_pending_list.push_back(fde);