switch from select to poll to deal with Android 9 giving us high socket fds when calling ::connect
This commit is contained in:
		| @@ -17,6 +17,7 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  | #include <poll.h> | ||||||
|  |  | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
|  |  | ||||||
| @@ -49,25 +50,21 @@ namespace ix | |||||||
|                                 int sockfd, |                                 int sockfd, | ||||||
|                                 std::shared_ptr<SelectInterrupt> selectInterrupt) |                                 std::shared_ptr<SelectInterrupt> selectInterrupt) | ||||||
|     { |     { | ||||||
|         fd_set rfds; |         // | ||||||
|         fd_set wfds; |         // We used to use ::select to poll but on Android 9 we get large fds out of ::connect | ||||||
|         fd_set efds; |         // which crash in FD_SET as they are larger than FD_SETSIZE. | ||||||
|         FD_ZERO(&rfds); |         // Switching to ::poll does fix that. | ||||||
|         FD_ZERO(&wfds); |         // | ||||||
|         FD_ZERO(&efds); |         // However poll isn't as portable as select and has bugs on Windows, so we should write a  | ||||||
|  |         // shim to fallback to select on those platforms. | ||||||
|  |         // See https://github.com/mpv-player/mpv/pull/5203/files for such a select wrapper. | ||||||
|  |         // | ||||||
|  |         int nfds = 1; | ||||||
|  |         struct pollfd fds[2]; | ||||||
|  |  | ||||||
|         // FD_SET cannot handle fds larger than FD_SETSIZE. |         fds[0].fd = sockfd; | ||||||
|         if (sockfd >= FD_SETSIZE) |         fds[0].events = (readyToRead) ? POLLIN : POLLOUT; | ||||||
|         { |         fds[0].events |= POLLERR; | ||||||
|             return PollResultType::Error; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fd_set* fds = (readyToRead) ? &rfds : & wfds; |  | ||||||
|         if (sockfd != -1) |  | ||||||
|         { |  | ||||||
|             FD_SET(sockfd, fds); |  | ||||||
|             FD_SET(sockfd, &efds); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // File descriptor used to interrupt select when needed |         // File descriptor used to interrupt select when needed | ||||||
|         int interruptFd = -1; |         int interruptFd = -1; | ||||||
| @@ -75,27 +72,15 @@ namespace ix | |||||||
|         { |         { | ||||||
|             interruptFd = selectInterrupt->getFd(); |             interruptFd = selectInterrupt->getFd(); | ||||||
|  |  | ||||||
|             // FD_SET cannot handle fds larger than FD_SETSIZE. |  | ||||||
|             if (interruptFd >= FD_SETSIZE) |  | ||||||
|             { |  | ||||||
|                 return PollResultType::Error; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             if (interruptFd != -1) |             if (interruptFd != -1) | ||||||
|             { |             { | ||||||
|                 FD_SET(interruptFd, fds); |                 nfds = 2; | ||||||
|  |                 fds[1].fd = interruptFd; | ||||||
|  |                 fds[1].events = POLLIN; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         struct timeval timeout; |         int ret = ::poll(fds, nfds, timeoutMs); | ||||||
|         timeout.tv_sec = timeoutMs / 1000; |  | ||||||
|         timeout.tv_usec = 1000 * (timeoutMs % 1000); |  | ||||||
|  |  | ||||||
|         // Compute the highest fd. |  | ||||||
|         int nfds = (std::max)(sockfd, interruptFd); |  | ||||||
|  |  | ||||||
|         int ret = ::select(nfds + 1, &rfds, &wfds, &efds, |  | ||||||
|                            (timeoutMs < 0) ? nullptr : &timeout); |  | ||||||
|  |  | ||||||
|         PollResultType pollResult = PollResultType::ReadyForRead; |         PollResultType pollResult = PollResultType::ReadyForRead; | ||||||
|         if (ret < 0) |         if (ret < 0) | ||||||
| @@ -106,7 +91,7 @@ namespace ix | |||||||
|         { |         { | ||||||
|             pollResult = PollResultType::Timeout; |             pollResult = PollResultType::Timeout; | ||||||
|         } |         } | ||||||
|         else if (interruptFd != -1 && FD_ISSET(interruptFd, &rfds)) |         else if (interruptFd != -1 && fds[1].revents & POLLIN) | ||||||
|         { |         { | ||||||
|             uint64_t value = selectInterrupt->read(); |             uint64_t value = selectInterrupt->read(); | ||||||
|  |  | ||||||
| @@ -119,17 +104,17 @@ namespace ix | |||||||
|                 pollResult = PollResultType::CloseRequest; |                 pollResult = PollResultType::CloseRequest; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else if (sockfd != -1 && readyToRead && FD_ISSET(sockfd, &rfds)) |         else if (sockfd != -1 && readyToRead && fds[0].revents & POLLIN) | ||||||
|         { |         { | ||||||
|             pollResult = PollResultType::ReadyForRead; |             pollResult = PollResultType::ReadyForRead; | ||||||
|         } |         } | ||||||
|         else if (sockfd != -1 && !readyToRead && FD_ISSET(sockfd, &wfds)) |         else if (sockfd != -1 && !readyToRead && fds[0].revents & POLLOUT) | ||||||
|         { |         { | ||||||
|             pollResult = PollResultType::ReadyForWrite; |             pollResult = PollResultType::ReadyForWrite; | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
|             // On connect error, in async mode, windows will write to the exceptions fds |             // On connect error, in async mode, windows will write to the exceptions fds | ||||||
|             if (FD_ISSET(fd, &efds)) |             if (fds[0].revents & POLLERR) | ||||||
|             { |             { | ||||||
|                 pollResult = PollResultType::Error; |                 pollResult = PollResultType::Error; | ||||||
|             } |             } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user