adb: move adb to poll.
It is reported that the registered fd can be bigger than FD_SETSIZE, and can't be
handled by select(). By moving to poll(), we can remove the limitation.
Although we can't ignore the possibility that there is a fd leak, but we can
still make the potential bug more explicit by moving to poll().
We didn't move to epoll() because it is not supported on mac.
Bug: 23820751
Change-Id: Icb39329c4984f1fef749472c9e088682ee8c3444
diff --git a/adb/adb.cpp b/adb/adb.cpp
index b260a78..1249822 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -177,6 +177,7 @@
{"jdwp", TRACE_JDWP},
{"services", TRACE_SERVICES},
{"auth", TRACE_AUTH},
+ {"fdevent", TRACE_FDEVENT},
{"shell", TRACE_SHELL}};
std::vector<std::string> elements = android::base::Split(trace_setting, " ");
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index db5157f..dc03807 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -20,59 +20,30 @@
#include "sysdeps.h"
#include "fdevent.h"
-#include <errno.h>
#include <fcntl.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
+#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
+#include <list>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
#include "adb_io.h"
#include "adb_trace.h"
-/* !!! Do not enable DEBUG for the adb that will run as the server:
-** both stdout and stderr are used to communicate between the client
-** and server. Any extra output will cause failures.
-*/
-#define DEBUG 0 /* non-0 will break adb server */
-
+#if !ADB_HOST
// This socket is used when a subproc shell service exists.
// It wakes up the fdevent_loop() and cause the correct handling
// of the shell's pseudo-tty master. I.e. force close it.
-#if !ADB_HOST
int SHELL_EXIT_NOTIFY_FD = -1;
#endif // !ADB_HOST
-static void fatal(const char *fn, const char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- fprintf(stderr, "%s:", fn);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- abort();
-}
-
-#define FATAL(x...) fatal(__FUNCTION__, x)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
- adb_mutex_lock(&D_lock);
- fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
- fde->state & FDE_READ ? 'R' : ' ',
- fde->state & FDE_WRITE ? 'W' : ' ',
- fde->state & FDE_ERROR ? 'E' : ' ',
- info);
- adb_mutex_unlock(&D_lock);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
#define FDE_EVENTMASK 0x00ff
#define FDE_STATEMASK 0xff00
@@ -80,515 +51,47 @@
#define FDE_PENDING 0x0200
#define FDE_CREATED 0x0400
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
+struct PollNode {
+ fdevent* fde;
+ pollfd pollfd;
-static fdevent list_pending = {
- .next = &list_pending,
- .prev = &list_pending,
- .fd = -1,
- .force_eof = 0,
- .state = 0,
- .events = 0,
- .func = nullptr,
- .arg = nullptr,
+ PollNode(fdevent* fde) : fde(fde) {
+ memset(&pollfd, 0, sizeof(pollfd));
+ pollfd.fd = fde->fd;
+ }
};
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
+// All operations to fdevent should happen only in the main thread.
+// That's why we don't need a lock for fdevent.
+static std::unordered_map<int, PollNode> g_poll_node_map;
+static std::list<fdevent*> g_pending_list;
-#ifdef CRAPTASTIC
-//HAVE_EPOLL
-
-#include <sys/epoll.h>
-
-static int epoll_fd = -1;
-
-static void fdevent_init()
-{
- /* XXX: what's a good size for the passed in hint? */
- epoll_fd = epoll_create(256);
-
- if(epoll_fd < 0) {
- perror("epoll_create() failed");
- exit(1);
+static std::string dump_fde(const fdevent* fde) {
+ std::string state;
+ if (fde->state & FDE_ACTIVE) {
+ state += "A";
}
-
- /* mark for close-on-exec */
- fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+ if (fde->state & FDE_PENDING) {
+ state += "P";
+ }
+ if (fde->state & FDE_CREATED) {
+ state += "C";
+ }
+ if (fde->state & FDE_READ) {
+ state += "R";
+ }
+ if (fde->state & FDE_WRITE) {
+ state += "W";
+ }
+ if (fde->state & FDE_ERROR) {
+ state += "E";
+ }
+ if (fde->state & FDE_DONT_CLOSE) {
+ state += "D";
+ }
+ return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
}
-static void fdevent_connect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
-#if 0
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
-#endif
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- struct epoll_event ev;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- /* technically we only need to delete if we
- ** were actively monitoring events, but let's
- ** be aggressive and do it anyway, just in case
- ** something's out of sync
- */
- epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- struct epoll_event ev;
- int active;
-
- active = (fde->state & FDE_EVENTMASK) != 0;
-
- memset(&ev, 0, sizeof(ev));
- ev.events = 0;
- ev.data.ptr = fde;
-
- if(events & FDE_READ) ev.events |= EPOLLIN;
- if(events & FDE_WRITE) ev.events |= EPOLLOUT;
- if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-
- if(active) {
- /* we're already active. if we're changing to *no*
- ** events being monitored, we need to delete, otherwise
- ** we need to just modify
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- } else {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
- } else {
- /* we're not active. if we're watching events, we need
- ** to add, otherwise we can just do nothing
- */
- if(ev.events) {
- if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
- perror("epoll_ctl() failed\n");
- exit(1);
- }
- }
- }
-}
-
-static void fdevent_process()
-{
- struct epoll_event events[256];
- fdevent *fde;
- int i, n;
-
- n = epoll_wait(epoll_fd, events, 256, -1);
-
- if (n < 0) {
- if (errno == EINTR) return;
- perror("epoll_wait");
- exit(1);
- }
-
- for(i = 0; i < n; i++) {
- struct epoll_event *ev = events + i;
- fde = ev->data.ptr;
-
- if(ev->events & EPOLLIN) {
- fde->events |= FDE_READ;
- }
- if(ev->events & EPOLLOUT) {
- fde->events |= FDE_WRITE;
- }
- if(ev->events & (EPOLLERR | EPOLLHUP)) {
- fde->events |= FDE_ERROR;
- }
- if(fde->events) {
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
-}
-
-#else /* USE_SELECT */
-
-#if defined(_WIN32)
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
-
-static fd_set read_fds;
-static fd_set write_fds;
-static fd_set error_fds;
-
-static int select_n = 0;
-
-static void fdevent_init(void)
-{
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- FD_ZERO(&error_fds);
-}
-
-static void fdevent_connect(fdevent *fde)
-{
- if(fde->fd >= select_n) {
- select_n = fde->fd + 1;
- }
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
- int i, n;
-
- FD_CLR(fde->fd, &read_fds);
- FD_CLR(fde->fd, &write_fds);
- FD_CLR(fde->fd, &error_fds);
-
- for(n = 0, i = 0; i < select_n; i++) {
- if(fd_table[i] != 0) n = i;
- }
- select_n = n + 1;
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
- if(events & FDE_READ) {
- FD_SET(fde->fd, &read_fds);
- } else {
- FD_CLR(fde->fd, &read_fds);
- }
- if(events & FDE_WRITE) {
- FD_SET(fde->fd, &write_fds);
- } else {
- FD_CLR(fde->fd, &write_fds);
- }
- if(events & FDE_ERROR) {
- FD_SET(fde->fd, &error_fds);
- } else {
- FD_CLR(fde->fd, &error_fds);
- }
-
- fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-/* Looks at fd_table[] for bad FDs and sets bit in fds.
-** Returns the number of bad FDs.
-*/
-static int fdevent_fd_check(fd_set *fds)
-{
- int i, n = 0;
- fdevent *fde;
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- if(fde == 0) continue;
- if(fcntl(i, F_GETFL, NULL) < 0) {
- FD_SET(i, fds);
- n++;
- // fde->state |= FDE_DONT_CLOSE;
-
- }
- }
- return n;
-}
-
-#if !DEBUG
-static inline void dump_all_fds(const char* /* extra_msg */) {}
-#else
-static void dump_all_fds(const char *extra_msg)
-{
-int i;
- fdevent *fde;
- // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
- char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
- size_t max_chars = FD_SETSIZE * 6 + 1;
- int printed_out;
-#define SAFE_SPRINTF(...) \
- do { \
- printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
- if (printed_out <= 0) { \
- D("... snprintf failed."); \
- return; \
- } \
- if (max_chars < (unsigned int)printed_out) { \
- D("... snprintf out of space."); \
- return; \
- } \
- pb += printed_out; \
- max_chars -= printed_out; \
- } while(0)
-
- for(i = 0; i < select_n; i++) {
- fde = fd_table[i];
- SAFE_SPRINTF("%d", i);
- if(fde == 0) {
- SAFE_SPRINTF("? ");
- continue;
- }
- if(fcntl(i, F_GETFL, NULL) < 0) {
- SAFE_SPRINTF("b");
- }
- SAFE_SPRINTF(" ");
- }
- D("%s fd_table[]->fd = {%s}", extra_msg, msg_buff);
-}
-#endif
-
-static void fdevent_process()
-{
- int i, n;
- fdevent *fde;
- unsigned events;
- fd_set rfd, wfd, efd;
-
- memcpy(&rfd, &read_fds, sizeof(fd_set));
- memcpy(&wfd, &write_fds, sizeof(fd_set));
- memcpy(&efd, &error_fds, sizeof(fd_set));
-
- dump_all_fds("pre select()");
-
- n = select(select_n, &rfd, &wfd, &efd, NULL);
- int saved_errno = errno;
- D("select() returned n=%d, errno=%d", n, n<0?saved_errno:0);
-
- dump_all_fds("post select()");
-
- if(n < 0) {
- switch(saved_errno) {
- case EINTR: return;
- case EBADF:
- // Can't trust the FD sets after an error.
- FD_ZERO(&wfd);
- FD_ZERO(&efd);
- FD_ZERO(&rfd);
- break;
- default:
- D("Unexpected select() error=%d", saved_errno);
- return;
- }
- }
- if(n <= 0) {
- // We fake a read, as the rest of the code assumes
- // that errors will be detected at that point.
- n = fdevent_fd_check(&rfd);
- }
-
- for(i = 0; (i < select_n) && (n > 0); i++) {
- events = 0;
- if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
- if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
- if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
-
- if(events) {
- fde = fd_table[i];
- if(fde == 0)
- FATAL("missing fde for fd %d\n", i);
-
- fde->events |= events;
-
- D("got events fde->fd=%d events=%04x, state=%04x",
- fde->fd, fde->events, fde->state);
- if(fde->state & FDE_PENDING) continue;
- fde->state |= FDE_PENDING;
- fdevent_plist_enqueue(fde);
- }
- }
-}
-
-#endif
-
-static void fdevent_register(fdevent *fde)
-{
- if(fde->fd < 0) {
- FATAL("bogus negative fd (%d)\n", fde->fd);
- }
-
- if(fde->fd >= fd_table_max) {
- int oldmax = fd_table_max;
- if(fde->fd > 32000) {
- FATAL("bogus huuuuge fd (%d)\n", fde->fd);
- }
- if(fd_table_max == 0) {
- fdevent_init();
- fd_table_max = 256;
- }
- while(fd_table_max <= fde->fd) {
- fd_table_max *= 2;
- }
- fd_table = reinterpret_cast<fdevent**>(
- realloc(fd_table, sizeof(fdevent*) * fd_table_max));
- if(fd_table == 0) {
- FATAL("could not expand fd_table to %d entries\n", fd_table_max);
- }
- memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
- }
-
- fd_table[fde->fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
- if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
- FATAL("fd out of range (%d)\n", fde->fd);
- }
-
- if(fd_table[fde->fd] != fde) {
- FATAL("fd_table out of sync [%d]\n", fde->fd);
- }
-
- fd_table[fde->fd] = 0;
-
- if(!(fde->state & FDE_DONT_CLOSE)) {
- dump_fde(fde, "close");
- adb_close(fde->fd);
- }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
- fdevent *list = &list_pending;
-
- node->next = list;
- node->prev = list->prev;
- node->prev->next = node;
- list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
- node->prev->next = node->next;
- node->next->prev = node->prev;
- node->next = 0;
- node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
- fdevent *list = &list_pending;
- fdevent *node = list->next;
-
- if(node == list) return 0;
-
- list->next = node->next;
- list->next->prev = list;
- node->next = 0;
- node->prev = 0;
-
- return node;
-}
-
-static void fdevent_call_fdfunc(fdevent* fde)
-{
- unsigned events = fde->events;
- fde->events = 0;
- if(!(fde->state & FDE_PENDING)) return;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
-}
-
-#if !ADB_HOST
-static void fdevent_subproc_event_func(int fd, unsigned ev,
- void* /* userdata */)
-{
-
- D("subproc handling on fd=%d ev=%04x", fd, ev);
-
- // Hook oneself back into the fde's suitable for select() on read.
- if((fd < 0) || (fd >= fd_table_max)) {
- FATAL("fd %d out of range for fd_table \n", fd);
- }
- fdevent *fde = fd_table[fd];
- fdevent_add(fde, FDE_READ);
-
- if(ev & FDE_READ){
- int subproc_fd;
-
- if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
- FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
- }
- if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
- D("subproc_fd %d out of range 0, fd_table_max=%d",
- subproc_fd, fd_table_max);
- return;
- }
- fdevent *subproc_fde = fd_table[subproc_fd];
- if(!subproc_fde) {
- D("subproc_fd %d cleared from fd_table", subproc_fd);
- return;
- }
- if(subproc_fde->fd != subproc_fd) {
- // Already reallocated?
- D("subproc_fd %d != fd_table[].fd %d", subproc_fd, subproc_fde->fd);
- return;
- }
-
- subproc_fde->force_eof = 1;
-
- int rcount = 0;
- ioctl(subproc_fd, FIONREAD, &rcount);
- D("subproc with fd=%d has rcount=%d err=%d",
- subproc_fd, rcount, errno);
-
- if(rcount) {
- // If there is data left, it will show up in the select().
- // This works because there is no other thread reading that
- // data when in this fd_func().
- return;
- }
-
- D("subproc_fde.state=%04x", subproc_fde->state);
- subproc_fde->events |= FDE_READ;
- if(subproc_fde->state & FDE_PENDING) {
- return;
- }
- subproc_fde->state |= FDE_PENDING;
- fdevent_call_fdfunc(subproc_fde);
- }
-}
-
-void fdevent_subproc_setup()
-{
- int s[2];
-
- if(adb_socketpair(s)) {
- FATAL("cannot create shell-exit socket-pair\n");
- }
- D("socketpair: (%d,%d)", s[0], s[1]);
-
- SHELL_EXIT_NOTIFY_FD = s[0];
- fdevent *fde;
- fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
- if(!fde)
- FATAL("cannot create fdevent for shell-exit handler\n");
- fdevent_add(fde, FDE_READ);
-}
-#endif // !ADB_HOST
-
fdevent *fdevent_create(int fd, fd_func func, void *arg)
{
fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
@@ -602,98 +105,234 @@
{
if(fde == 0) return;
if(!(fde->state & FDE_CREATED)) {
- FATAL("fde %p not created by fdevent_create()\n", fde);
+ LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
}
fdevent_remove(fde);
free(fde);
}
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
+void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+ CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
fde->fd = fd;
- fde->force_eof = 0;
fde->func = func;
fde->arg = arg;
-
-#if !defined(_WIN32)
- fcntl(fd, F_SETFL, O_NONBLOCK);
-#endif
- fdevent_register(fde);
- dump_fde(fde, "connect");
- fdevent_connect(fde);
- fde->state |= FDE_ACTIVE;
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
+ // Here is not proper to handle the error. If it fails here, some error is
+ // likely to be detected by poll(), then we can let the callback function
+ // to handle it.
+ LOG(ERROR) << "failed to fcntl(" << fd << ") to be nonblock";
+ }
+ auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+ CHECK(pair.second) << "install existing fd " << fd;
+ D("fdevent_install %s", dump_fde(fde).c_str());
}
-void fdevent_remove(fdevent *fde)
-{
- if(fde->state & FDE_PENDING) {
- fdevent_plist_remove(fde);
+void fdevent_remove(fdevent* fde) {
+ D("fdevent_remove %s", dump_fde(fde).c_str());
+ if (fde->state & FDE_ACTIVE) {
+ g_poll_node_map.erase(fde->fd);
+ if (fde->state & FDE_PENDING) {
+ g_pending_list.remove(fde);
+ }
+ if (!(fde->state & FDE_DONT_CLOSE)) {
+ adb_close(fde->fd);
+ fde->fd = -1;
+ }
+ fde->state = 0;
+ fde->events = 0;
}
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_disconnect(fde);
- dump_fde(fde, "disconnect");
- fdevent_unregister(fde);
- }
-
- fde->state = 0;
- fde->events = 0;
}
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
- events &= FDE_EVENTMASK;
-
- if((fde->state & FDE_EVENTMASK) == events) return;
-
- if(fde->state & FDE_ACTIVE) {
- fdevent_update(fde, events);
- dump_fde(fde, "update");
+static void fdevent_update(fdevent* fde, unsigned events) {
+ auto it = g_poll_node_map.find(fde->fd);
+ CHECK(it != g_poll_node_map.end());
+ PollNode& node = it->second;
+ if (events & FDE_READ) {
+ node.pollfd.events |= POLLIN;
+ } else {
+ node.pollfd.events &= ~POLLIN;
}
+ if (events & FDE_WRITE) {
+ node.pollfd.events |= POLLOUT;
+ } else {
+ node.pollfd.events &= ~POLLOUT;
+ }
fde->state = (fde->state & FDE_STATEMASK) | events;
+}
- if(fde->state & FDE_PENDING) {
- /* if we're pending, make sure
- ** we don't signal an event that
- ** is no longer wanted.
- */
- fde->events &= (~events);
- if(fde->events == 0) {
- fdevent_plist_remove(fde);
- fde->state &= (~FDE_PENDING);
+void fdevent_set(fdevent* fde, unsigned events) {
+ events &= FDE_EVENTMASK;
+ if ((fde->state & FDE_EVENTMASK) == events) {
+ return;
+ }
+ if (fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+ if (fde->state & FDE_PENDING) {
+ // If we are pending, make sure we don't signal an event that is no longer wanted.
+ fde->events &= ~events;
+ if (fde->events == 0) {
+ g_pending_list.remove(fde);
+ fde->state &= ~FDE_PENDING;
+ }
}
}
}
-void fdevent_add(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+void fdevent_add(fdevent* fde, unsigned events) {
+ fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
}
-void fdevent_del(fdevent *fde, unsigned events)
-{
- fdevent_set(
- fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+void fdevent_del(fdevent* fde, unsigned events) {
+ fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
}
+static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
+ std::string result;
+ for (auto& pollfd : pollfds) {
+ std::string op;
+ if (pollfd.events & POLLIN) {
+ op += "R";
+ }
+ if (pollfd.events & POLLOUT) {
+ op += "W";
+ }
+ android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+ }
+ return result;
+}
+
+static void fdevent_process() {
+ std::vector<pollfd> pollfds;
+ for (auto it = g_poll_node_map.begin(); it != g_poll_node_map.end(); ++it) {
+ pollfds.push_back(it->second.pollfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+ int ret = TEMP_FAILURE_RETRY(poll(&pollfds[0], pollfds.size(), -1));
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+ for (auto& pollfd : pollfds) {
+ unsigned events = 0;
+ if (pollfd.revents & POLLIN) {
+ events |= FDE_READ;
+ }
+ if (pollfd.revents & POLLOUT) {
+ events |= FDE_WRITE;
+ }
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+ 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, pollfd.fd);
+ fde->events |= events;
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ fde->state |= FDE_PENDING;
+ g_pending_list.push_back(fde);
+ }
+ }
+}
+
+static void fdevent_call_fdfunc(fdevent* fde)
+{
+ unsigned events = fde->events;
+ fde->events = 0;
+ if(!(fde->state & FDE_PENDING)) return;
+ fde->state &= (~FDE_PENDING);
+ D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
+ fde->func(fde->fd, events, fde->arg);
+}
+
+#if !ADB_HOST
+static void fdevent_subproc_event_func(int fd, unsigned ev,
+ void* /* userdata */)
+{
+
+ D("subproc handling on fd = %d, ev = %x", fd, ev);
+
+ CHECK_GE(fd, 0);
+
+ if (ev & FDE_READ) {
+ int subproc_fd;
+
+ if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
+ LOG(FATAL) << "Failed to read the subproc's fd from " << fd;
+ }
+ auto it = g_poll_node_map.find(subproc_fd);
+ if (it == g_poll_node_map.end()) {
+ D("subproc_fd %d cleared from fd_table", subproc_fd);
+ return;
+ }
+ fdevent* subproc_fde = it->second.fde;
+ if(subproc_fde->fd != subproc_fd) {
+ // Already reallocated?
+ D("subproc_fd(%d) != subproc_fde->fd(%d)", subproc_fd, subproc_fde->fd);
+ return;
+ }
+
+ subproc_fde->force_eof = 1;
+
+ int rcount = 0;
+ ioctl(subproc_fd, FIONREAD, &rcount);
+ D("subproc with fd %d has rcount=%d, err=%d", subproc_fd, rcount, errno);
+ if (rcount != 0) {
+ // If there is data left, it will show up in the select().
+ // This works because there is no other thread reading that
+ // data when in this fd_func().
+ return;
+ }
+
+ D("subproc_fde %s", dump_fde(subproc_fde).c_str());
+ subproc_fde->events |= FDE_READ;
+ if(subproc_fde->state & FDE_PENDING) {
+ return;
+ }
+ subproc_fde->state |= FDE_PENDING;
+ fdevent_call_fdfunc(subproc_fde);
+ }
+}
+
+void fdevent_subproc_setup()
+{
+ int s[2];
+
+ if(adb_socketpair(s)) {
+ PLOG(FATAL) << "cannot create shell-exit socket-pair";
+ }
+ D("fdevent_subproc: socket pair (%d, %d)", s[0], s[1]);
+
+ SHELL_EXIT_NOTIFY_FD = s[0];
+ fdevent *fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+ CHECK(fde != nullptr) << "cannot create fdevent for shell-exit handler";
+ fdevent_add(fde, FDE_READ);
+}
+#endif // !ADB_HOST
+
void fdevent_loop()
{
- fdevent *fde;
#if !ADB_HOST
fdevent_subproc_setup();
#endif // !ADB_HOST
while (true) {
- D("--- ---- waiting for events");
+ D("--- --- waiting for events");
fdevent_process();
- while((fde = fdevent_plist_dequeue())) {
+ while (!g_pending_list.empty()) {
+ fdevent* fde = g_pending_list.front();
+ g_pending_list.pop_front();
fdevent_call_fdfunc(fde);
}
}
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index f7622b5..33034d8 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,6 +18,7 @@
#include <gtest/gtest.h>
+#include <limits>
#include <queue>
#include <string>
#include <vector>
@@ -50,9 +51,13 @@
public:
FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
- fdevent_add(&read_fde_, FDE_READ | FDE_ERROR);
+ fdevent_add(&read_fde_, FDE_READ);
fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
- fdevent_add(&write_fde_, FDE_ERROR);
+ }
+
+ ~FdHandler() {
+ fdevent_remove(&read_fde_);
+ fdevent_remove(&write_fde_);
}
private:
@@ -150,3 +155,44 @@
ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
ASSERT_EQ(0, pthread_join(thread, nullptr));
}
+
+struct InvalidFdArg {
+ fdevent fde;
+ unsigned expected_events;
+ size_t* happened_event_count;
+};
+
+static void InvalidFdEventCallback(int fd, unsigned events, void* userdata) {
+ InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
+ ASSERT_EQ(arg->expected_events, events);
+ fdevent_remove(&arg->fde);
+ if (++*(arg->happened_event_count) == 2) {
+ pthread_exit(nullptr);
+ }
+}
+
+void InvalidFdThreadFunc(void*) {
+ const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
+ size_t happened_event_count = 0;
+ InvalidFdArg read_arg;
+ read_arg.expected_events = FDE_READ | FDE_ERROR;
+ read_arg.happened_event_count = &happened_event_count;
+ fdevent_install(&read_arg.fde, INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
+ fdevent_add(&read_arg.fde, FDE_READ);
+
+ const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
+ InvalidFdArg write_arg;
+ write_arg.expected_events = FDE_READ | FDE_ERROR;
+ write_arg.happened_event_count = &happened_event_count;
+ fdevent_install(&write_arg.fde, INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+ fdevent_add(&write_arg.fde, FDE_WRITE);
+ fdevent_loop();
+}
+
+TEST(fdevent, invalid_fd) {
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr,
+ reinterpret_cast<void* (*)(void*)>(InvalidFdThreadFunc),
+ nullptr));
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+}