Avoid spawning thread for trivial getnameinfo calls

When calling getnameinfo we spawn a thread because it may do a slow,
blocking reverse-DNS lookup. Spawning a thread is relatively fast (~20µs
on my Linux machine) but still an order of magnitude slower than when
getnameinfo is simply translating to a numeric IP or port, which, at
least in my tests on Linux, doesn't even make a syscall.

This commit adds a fast path for when reverse DNS isn't required: either
host isn't being fetched or NI_NUMERICHOST is set AND either the
service name isn't required or NI_NUMERICSERV is set. The service name
should only need to read /etc/services, which should be fast-ish, but
is still I/O so I kept the existing behaviour (it could be on a network
fs I guess).

I tested with:

    s = TCPSocket.open("www.ruby-lang.org", 80)
    500_000.times { Socket.unpack_sockaddr_in(s.getpeername) }

Before: 12.935s
After: 0.338s
This commit is contained in:
John Hawthorn 2025-08-19 19:13:00 -07:00
parent 9d484e3412
commit 5c96bbf36a

View File

@ -578,6 +578,10 @@ start:
#endif
#define GETNAMEINFO_WONT_BLOCK(host, serv, flags) \
((!(host) || ((flags) & NI_NUMERICHOST)) && \
(!(serv) || ((flags) & NI_NUMERICSERV)))
#if GETADDRINFO_IMPL == 0
int
@ -615,6 +619,10 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *serv, size_t servlen, int flags)
{
if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) {
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
struct getnameinfo_arg arg;
int ret;
arg.sa = sa;
@ -743,6 +751,10 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
struct getnameinfo_arg *arg;
int err = 0, gni_errno = 0;
if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) {
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
start:
retry = 0;