From b07fa8c110cc16d58b41c1cc2bc4492486faef5b Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 19 Mar 2023 18:34:43 -0700 Subject: [PATCH] ircd::net: Add interface for native_non_blocking(); mitigate unconditional ioctl. --- include/ircd/net/sock_opts.h | 3 +++ ircd/net.cc | 37 ++++++++++++++++++++++++++++++++++-- ircd/net_listener.cc | 5 +++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/ircd/net/sock_opts.h b/include/ircd/net/sock_opts.h index 69675e783..2fda9802b 100644 --- a/include/ircd/net/sock_opts.h +++ b/include/ircd/net/sock_opts.h @@ -14,8 +14,10 @@ namespace ircd::net { struct sock_opts; + IRCD_OVERLOAD(system); bool v6only(const socket &); + bool blocking(const socket &, system_t); bool blocking(const socket &); bool nopush(const socket &); bool nodelay(const socket &); @@ -30,6 +32,7 @@ namespace ircd::net // returns true if supported, false if unsupported; failures will throw. bool v6only(socket &, const bool); + bool blocking(socket &, const bool, system_t); bool blocking(socket &, const bool); bool nopush(socket &, const bool); bool nodelay(socket &, const bool); diff --git a/ircd/net.cc b/ircd/net.cc index 011677886..98db29df8 100644 --- a/ircd/net.cc +++ b/ircd/net.cc @@ -1114,8 +1114,11 @@ ircd::net::nopush(socket &socket, /// Toggles the behavior of non-async asio calls. /// /// This option affects very little in practice and only sets a flag in -/// userspace in asio, not an actual ioctl(). Specifically: +/// userspace in asio, not an actual ioctl(2) (XXX this is not true anymore, +/// sd.non_blocking() and sd.native_non_blocking() both seem to ioctl(2)). +/// See below the deprecated section. /// +/// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ /// * All sockets are already set by asio to FIONBIO=1 no matter what, thus /// nothing really blocks the event loop ever by default unless you try hard. /// @@ -1134,13 +1137,35 @@ ircd::net::nopush(socket &socket, /// in this project there is never a reason to ever set this to true, /// however, sockets do get constructed by asio in blocking mode by default /// so we mostly use this function to set it to non-blocking. +/// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ /// +/// The kern argument has been added to decide between native_non_blocking() +/// (when kern=true) or non_blocking() (when kern=false). These both set +/// different flags in asio but they both result in the same ioctl(FIONBIO) +/// probably due to third-party libraries flipping FIONBIO outside of asio's +/// knowledge and naive users complaining too much to the maintainer. +/// +/// To deal with this we have added a query to the sd.non_blocking() getter +/// which AT LEAST FOR NOW only reads asio's flags without a syscall and +/// won't call the sd.non_blocking() setter if it's superfluous. bool ircd::net::blocking(socket &socket, const bool b) { ip::tcp::socket &sd(socket); - sd.non_blocking(!b); + if(likely(sd.non_blocking() == b)) + sd.non_blocking(!b); + + return true; +} + +bool +ircd::net::blocking(socket &socket, + const bool b, + system_t) +{ + ip::tcp::socket &sd(socket); + sd.native_non_blocking(!b); return true; } @@ -1292,6 +1317,14 @@ ircd::net::blocking(const socket &socket) return !sd.non_blocking(); } +bool +ircd::net::blocking(const socket &socket, + system_t) +{ + const ip::tcp::socket &sd(socket); + return !sd.native_non_blocking(); +} + bool ircd::net::v6only(const socket &socket) { diff --git a/ircd/net_listener.cc b/ircd/net_listener.cc index 828f6c340..bf2f54605 100644 --- a/ircd/net_listener.cc +++ b/ircd/net_listener.cc @@ -1070,8 +1070,13 @@ ircd::net::acceptor::accepted(const std::shared_ptr &sock) assert(bool(cb)); assert(bool(sock)); + #if !defined(BSD_BASED_OS) // Toggles the behavior of non-async functions; see func comment + // This is not needed on BSD because the socket inherits the listener's + // non-blocking disposition. blocking(*sock, false); + #endif + cb(*this, sock); }