From 95fd9a4ae2717a73bc446b29a87ad5bc9f00317e Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Fri, 30 Oct 2026 02:35:32 +0100 Subject: Reduce number of per connection sys calls --- ot_event.c | 14 ++++++++++++++ ot_event.h | 5 +++++ ot_http.c | 3 +-- ot_socket.c | 13 ++++++++++--- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/ot_event.c b/ot_event.c index 1542bf5..8f55817 100644 --- a/ot_event.c +++ b/ot_event.c @@ -71,6 +71,10 @@ int event_add(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CT int event_modify(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CTL_MOD, fd, what, generation); } int event_del(int fd) { return epoll_ctl(g_epoll, EPOLL_CTL_DEL, fd, NULL); } +/* A descriptor leaves the interest list when the last reference to its open + file description is closed, and the tracker never duplicates one */ +int event_close(int fd) { return close(fd); } + int event_wait(ot_event *out, int max, int timeout_ms) { int count, i; @@ -171,6 +175,9 @@ int event_add(int fd, int what, uint32_t generation) { return event_change(fd, w int event_modify(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); } int event_del(int fd) { return event_change(fd, 0, 0, 1); } +/* Closing a descriptor removes every kevent that references it */ +int event_close(int fd) { return close(fd); } + int event_wait(ot_event *out, int max, int timeout_ms) { struct timespec timeout; struct timespec *deadline = NULL; @@ -311,6 +318,13 @@ int event_del(int fd) { return 0; } +/* Nothing here lives in the kernel, so the entry has to come out by hand + before the descriptor goes, or poll reports POLLNVAL for it forever */ +int event_close(int fd) { + event_del(fd); + return close(fd); +} + int event_wait(ot_event *out, int max, int timeout_ms) { int ready, found = 0, i; diff --git a/ot_event.h b/ot_event.h index 6023134..aa007d0 100644 --- a/ot_event.h +++ b/ot_event.h @@ -29,6 +29,11 @@ int event_add(int fd, int what, uint32_t generation); int event_modify(int fd, int what, uint32_t generation); int event_del(int fd); +/* Stop watching a descriptor and close it. Where the kernel drops a closed + descriptor from its set by itself, this is one system call where doing the + two separately would be two. */ +int event_close(int fd); + /* Waits at most timeout_ms, or indefinitely when that is negative, and fills out with up to max ready descriptors. Returns how many, or -1. */ int event_wait(ot_event *out, int max, int timeout_ms); diff --git a/ot_http.c b/ot_http.c index c2f7e5e..d4c7363 100644 --- a/ot_http.c +++ b/ot_http.c @@ -56,8 +56,7 @@ void http_close(const int64_t sock) { free(cookie); } - event_del((int)sock); - close((int)sock); + event_close((int)sock); } static void http_senddata(const int64_t sock, struct ot_workstruct *ws) { diff --git a/ot_socket.c b/ot_socket.c index 158459f..47ed26d 100644 --- a/ot_socket.c +++ b/ot_socket.c @@ -4,6 +4,7 @@ $id$ */ /* System */ +#define _GNU_SOURCE /* accept4 on glibc */ #include #include #include @@ -170,19 +171,25 @@ int socket_is_connected(int s) { return 1; } +/* Linux hands over a blocking socket whatever the listening socket was, the + BSDs and macOS inherit the listener's flags, so the accepted socket has to + be told either way. accept4 carries the flag itself, which saves the two + fcntl calls it otherwise takes. Darwin has neither accept4 nor the flag. */ int socket_accept(int s, ot_ip6 ip, uint16_t *port) { struct sockaddr_storage ss; socklen_t len = sizeof(ss); int fd; memset(&ss, 0, sizeof(ss)); +#ifdef SOCK_NONBLOCK + if ((fd = accept4(s, (struct sockaddr *)&ss, &len, SOCK_NONBLOCK)) == -1) + return -1; +#else if ((fd = accept(s, (struct sockaddr *)&ss, &len)) == -1) return -1; - - /* Linux hands over a blocking socket whatever the listening socket was, the - BSDs and macOS inherit the listener's flags, so say which one we want. */ if (set_nonblocking(fd) == -1) return close_and_fail(fd); +#endif ip_from_sockaddr(ip, port, &ss); return fd; -- cgit v1.3