summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar messi2022-10-24 13:37:58 +0200
committerGravatar Nikias Bassen2023-12-26 14:51:26 +0100
commitbbc3c4e58f7991495e885b38a03fd20abecd4583 (patch)
tree7b760fbf444d3246ca3dbec9346b8b07c3b63d79
parent2ae342bdedffbcd89a0e3607bf8f2e3d2316237a (diff)
downloadlibimobiledevice-glue-bbc3c4e58f7991495e885b38a03fd20abecd4583.tar.gz
libimobiledevice-glue-bbc3c4e58f7991495e885b38a03fd20abecd4583.tar.bz2
socket: Fix select failing when the process has many file descriptors by using poll instead
This happens, for example, when a process links with libusbmuxd, and the process is file descriptor heavy. Note: 'poll' means the syscall poll, not a polling approach. This patch does not add a busy wait and is not a performance regression.
-rw-r--r--src/socket.c179
1 files changed, 100 insertions, 79 deletions
diff --git a/src/socket.c b/src/socket.c
index 48eb831..49a5ef4 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -65,6 +65,7 @@ static int wsa_init = 0;
#endif
#include "common.h"
#include "libimobiledevice-glue/socket.h"
+#include <sys/poll.h>
#define RECV_TIMEOUT 20000
#define SEND_TIMEOUT 10000
@@ -140,6 +141,80 @@ LIBIMOBILEDEVICE_GLUE_API const char *socket_addr_to_string(struct sockaddr *add
return NULL;
}
+enum poll_status
+{
+ poll_status_success,
+ poll_status_timeout,
+ poll_status_error
+};
+
+// timeoutMilliseconds of -1 means infinity
+static enum poll_status poll_wrapper(int fd, fd_mode mode, int timeoutMilliseconds)
+{
+ // https://man7.org/linux/man-pages/man2/select.2.html
+ // Correspondence between select() and poll() notifications
+ // #define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
+ // EPOLLHUP | EPOLLERR)
+ // /* Ready for reading */
+ // #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |
+ // EPOLLERR)
+ // /* Ready for writing */
+ // #define POLLEX_SET (EPOLLPRI)
+ // /* Exceptional condition */
+
+ short events;
+ switch(mode)
+ {
+ case FDM_READ:
+ events = POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR;
+ break;
+ case FDM_WRITE:
+ events = POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR;
+ break;
+ case FDM_EXCEPT:
+ events = POLLPRI;
+ break;
+ default:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: fd_mode %d unsupported\n", __func__, mode);
+ return poll_status_error;
+ }
+ while(1)
+ {
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = events,
+ };
+ switch(poll(&pfd, 1, timeoutMilliseconds))
+ {
+ case 1:
+ if((pfd.revents & (POLLNVAL | POLLERR)) != 0)
+ {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: poll unexpected events: %d\n", __func__,
+ (int)pfd.revents);
+ return poll_status_error;
+ }
+ return poll_status_success;
+ case 0:
+ return poll_status_timeout;
+ case -1:
+ if(errno == EINTR)
+ {
+ if (verbose >= 2)
+ fprintf(stderr, "%s: EINTR\n", __func__);
+ continue;
+ }
+ // fallthrough
+ default:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: poll failed: %s\n", __func__,
+ strerror(errno));
+ return poll_status_error;
+ }
+ }
+}
+
#ifndef WIN32
LIBIMOBILEDEVICE_GLUE_API int socket_create_unix(const char *filename)
{
@@ -246,14 +321,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_unix(const char *filename)
break;
}
if (errno == EINPROGRESS) {
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(sfd, &fds);
-
- struct timeval timeout;
- timeout.tv_sec = CONNECT_TIMEOUT / 1000;
- timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000;
- if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) {
+ if(poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) {
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len);
@@ -930,14 +998,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect_addr(struct sockaddr* addr, uint16_
if (errno == EINPROGRESS)
#endif
{
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(sfd, &fds);
-
- struct timeval timeout;
- timeout.tv_sec = CONNECT_TIMEOUT / 1000;
- timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000;
- if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) {
+ if(poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) {
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len);
@@ -1054,14 +1115,7 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port)
if (errno == EINPROGRESS)
#endif
{
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(sfd, &fds);
-
- struct timeval timeout;
- timeout.tv_sec = CONNECT_TIMEOUT / 1000;
- timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000;
- if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) {
+ if(poll_wrapper(sfd, FDM_WRITE, CONNECT_TIMEOUT) == poll_status_success) {
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len);
@@ -1098,70 +1152,37 @@ LIBIMOBILEDEVICE_GLUE_API int socket_connect(const char *addr, uint16_t port)
LIBIMOBILEDEVICE_GLUE_API int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout)
{
- fd_set fds;
- int sret;
- int eagain;
- struct timeval to;
- struct timeval *pto;
-
if (fd < 0) {
if (verbose >= 2)
fprintf(stderr, "ERROR: invalid fd in check_fd %d\n", fd);
return -1;
}
- FD_ZERO(&fds);
- FD_SET(fd, &fds);
-
- sret = -1;
-
- do {
- if (timeout > 0) {
- to.tv_sec = (time_t) (timeout / 1000);
- to.tv_usec = (time_t) ((timeout - (to.tv_sec * 1000)) * 1000);
- pto = &to;
- } else {
- pto = NULL;
- }
- eagain = 0;
- switch (fdm) {
- case FDM_READ:
- sret = select(fd + 1, &fds, NULL, NULL, pto);
- break;
- case FDM_WRITE:
- sret = select(fd + 1, NULL, &fds, NULL, pto);
- break;
- case FDM_EXCEPT:
- sret = select(fd + 1, NULL, NULL, &fds, pto);
- break;
- default:
- return -1;
- }
+ int timeoutMilliseconds;
+ if(timeout > 0)
+ {
+ timeoutMilliseconds = (int)timeout;
+ if(timeoutMilliseconds <= 0)
+ timeoutMilliseconds = -1;
+ }
+ else
+ {
+ timeoutMilliseconds = -1;
+ }
- if (sret < 0) {
- switch (errno) {
- case EINTR:
- // interrupt signal in select
- if (verbose >= 2)
- fprintf(stderr, "%s: EINTR\n", __func__);
- eagain = 1;
- break;
- case EAGAIN:
- if (verbose >= 2)
- fprintf(stderr, "%s: EAGAIN\n", __func__);
- break;
- default:
- if (verbose >= 2)
- fprintf(stderr, "%s: select failed: %s\n", __func__,
- strerror(errno));
- return -1;
- }
- } else if (sret == 0) {
+ switch(poll_wrapper(fd, fdm, timeoutMilliseconds))
+ {
+ case poll_status_success:
+ return 1;
+ case poll_status_timeout:
return -ETIMEDOUT;
- }
- } while (eagain);
+ case poll_status_error:
+ if (verbose >= 2)
+ fprintf(stderr, "%s: poll_wrapper failed\n", __func__);
+ return -1;
+ }
- return sret;
+ return -1;
}
LIBIMOBILEDEVICE_GLUE_API int socket_accept(int fd, uint16_t port)