]> git.meshlink.io Git - meshlink/commitdiff
Try to recover from select() returning an error.
authorGuus Sliepen <guus@meshlink.io>
Tue, 9 Feb 2021 20:00:35 +0000 (21:00 +0100)
committerGuus Sliepen <guus@meshlink.io>
Tue, 9 Feb 2021 20:21:45 +0000 (21:21 +0100)
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
src/net_setup.c
src/net_socket.c

index a7c1f25a3f3b9b05edd94a6ee579b818dd5b3412..82133eb471826ce1d9921d0f4a3fbac83df9387a 100644 (file)
@@ -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;
                }
index 269b46ed0d4921d08bad76c9478d301b213ed95a..6168c4ca340dfc92f9551c2c17f78eb624600758 100644 (file)
@@ -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);
index abf6a7cf0e0eb7fa878f347efa45a6910c646927..22cbe26fa08cc5725372517b42e829b8218dbcce 100644 (file)
@@ -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;