From 56b4ea7e7a8fe29c1c1066def42c792cb51540d0 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Tue, 9 Feb 2021 21:00:35 +0100 Subject: [PATCH] Try to recover from select() returning an error. If we get an error back from select(), it could be because one of the filedescriptors is in a bad state. This shouldn't occur, but if it does, just call all the registered I/O callbacks to have them check for errors. Only quit the event loop if the error persists. Also fix some I/O callbacks that didn't expect to be called when the filedescriptor isn't signalled. --- src/event.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++- src/net_setup.c | 27 ++++++++++++++++++---- src/net_socket.c | 4 ++++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/event.c b/src/event.c index a7c1f25a..82133eb4 100644 --- a/src/event.c +++ b/src/event.c @@ -211,6 +211,10 @@ static void pipe_init(event_loop_t *loop) { assert(result == 0); if(result == 0) { +#ifdef O_NONBLOCK + fcntl(loop->pipefd[0], F_SETFL, O_NONBLOCK); + fcntl(loop->pipefd[1], F_SETFL, O_NONBLOCK); +#endif io_add(loop, &loop->signalio, signalio_handler, NULL, loop->pipefd[0], IO_READ); } } @@ -278,11 +282,56 @@ void idle_set(event_loop_t *loop, idle_cb_t cb, void *data) { loop->idle_data = data; } +static void check_bad_fds(event_loop_t *loop) { + // Just call all registered callbacks and have them check their fds + + do { + loop->deletion = false; + + for splay_each(io_t, io, &loop->ios) { + if(io->flags & IO_WRITE) { + io->cb(loop, io->data, IO_WRITE); + } + + if(loop->deletion) { + break; + } + + if(io->flags & IO_READ) { + io->cb(loop, io->data, IO_READ); + } + + if(loop->deletion) { + break; + } + } + } while(loop->deletion); + + // Rebuild the fdsets + + memset(&loop->readfds, 0, sizeof(loop->readfds)); + memset(&loop->writefds, 0, sizeof(loop->writefds)); + + for splay_each(io_t, io, &loop->ios) { + if(io->flags & IO_READ) { + FD_SET(io->fd, &loop->readfds); + io->cb(loop, io->data, IO_READ); + } + + if(io->flags & IO_WRITE) { + FD_SET(io->fd, &loop->writefds); + io->cb(loop, io->data, IO_WRITE); + } + + } +} + bool event_loop_run(event_loop_t *loop, pthread_mutex_t *mutex) { assert(mutex); fd_set readable; fd_set writable; + int errors = 0; while(loop->running) { clock_gettime(EVENT_CLOCK, &loop->now); @@ -338,10 +387,19 @@ bool event_loop_run(event_loop_t *loop, pthread_mutex_t *mutex) { if(sockwouldblock(errno)) { continue; } else { - return false; + errors++; + + if(errors > 10) { + return false; + } + + check_bad_fds(loop); + continue; } } + errors = 0; + if(!n) { continue; } diff --git a/src/net_setup.c b/src/net_setup.c index 269b46ed..6168c4ca 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -332,6 +332,25 @@ int setup_tcp_listen_socket(meshlink_handle_t *mesh, const struct addrinfo *aip) fcntl(nfd, F_SETFD, FD_CLOEXEC); #endif +#ifdef O_NONBLOCK + int flags = fcntl(nfd, F_GETFL); + + if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) { + closesocket(nfd); + logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl", strerror(errno)); + return -1; + } + +#elif defined(WIN32) + unsigned long arg = 1; + + if(ioctlsocket(nfd, FIONBIO, &arg) != 0) { + closesocket(nfd); + logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno)); + return -1; + } + +#endif int option = 1; setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option)); @@ -528,8 +547,8 @@ static bool add_listen_sockets(meshlink_handle_t *mesh) { for(int i = 0; i < mesh->listen_sockets; i++) { io_del(&mesh->loop, &mesh->listen_socket[i].tcp); io_del(&mesh->loop, &mesh->listen_socket[i].udp); - close(mesh->listen_socket[i].tcp.fd); - close(mesh->listen_socket[i].udp.fd); + closesocket(mesh->listen_socket[i].tcp.fd); + closesocket(mesh->listen_socket[i].udp.fd); } mesh->listen_sockets = 0; @@ -617,8 +636,8 @@ void close_network_connections(meshlink_handle_t *mesh) { for(int i = 0; i < mesh->listen_sockets; i++) { io_del(&mesh->loop, &mesh->listen_socket[i].tcp); io_del(&mesh->loop, &mesh->listen_socket[i].udp); - close(mesh->listen_socket[i].tcp.fd); - close(mesh->listen_socket[i].udp.fd); + closesocket(mesh->listen_socket[i].tcp.fd); + closesocket(mesh->listen_socket[i].udp.fd); } exit_requests(mesh); diff --git a/src/net_socket.c b/src/net_socket.c index abf6a7cf..22cbe26f 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -524,6 +524,10 @@ void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) { fd = accept(l->tcp.fd, &sa.sa, &len); if(fd < 0) { + if(sockwouldblock(errno)) { + return; + } + if(errno == EINVAL) { // TODO: check if Windows agrees event_loop_stop(loop); return; -- 2.39.5