From 7e3182ae89b83bddfa7e452b1db7d8ee3b140e0a Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Tue, 27 Oct 2026 19:46:10 +0300 Subject: [PATCH] Event loops: the C library's resolver run on the thread's stack glibc's getaddrinfo() sizes what it takes with alloca(), IPv6 most, by the thread's stack, not by the fiber's it may be called on: a client moved to a new configuration on a reload goes on on its fiber, and the new configuration's services may resolve with the system resolver, which otherwise is never called on a loop (threadperclient). A fiber yields to have it called on the loop thread's own stack (onthreadstack()), and goes on with its answer; the loop waits for it as the fiber would have. Co-Authored-By: Claude Opus 5.5 --- src/common.c | 68 +++++++++++++++++++++++++++++++++++--------------- src/mainloop.c | 28 ++++++++++++++++++++- src/proxy.h | 1 + 3 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/common.c b/src/common.c index b3844d0..9456a8f 100644 --- a/src/common.c +++ b/src/common.c @@ -340,6 +340,8 @@ void (*fiberblockfunc)(SOCKET s, short events) = NULL; void (*fiberclosefunc)(SOCKET s) = NULL; /* set as well: whether this thread is a loop's, which is not to wait */ int (*onloopfunc)(void) = NULL; +/* and what runs func(arg) on the thread's own stack, not a fiber's */ +void (*onthreadstackfunc)(void (*func)(void *arg), void *arg) = NULL; #define BLOCKED(s, ev) do { if(fiberblockfunc && (errno == EAGAIN || errno == EWOULDBLOCK)) \ fiberblockfunc((s), (ev)); } while(0) @@ -1174,12 +1176,45 @@ int afdetect(unsigned char *name){ } /* A name resolved the way a mode says: a service's names with its own */ +#if !defined(NOIPV6) && !defined(NOSTDRESOLVE) +/* What the C library's resolver is asked, and its answer */ +struct stdresolve { + unsigned char *name; + int family; + struct sockaddr *sa; + uint32_t res; +}; + +/* getaddrinfo(): run on a thread's own stack, which glibc may take much of, + for IPv6 most (alloca() sized by the thread's stack, not a fiber's) */ +static void stdresolve46(void *arg){ + struct stdresolve *r = (struct stdresolve *)arg; + struct addrinfo *ai, hint; + + r->res = 0; + memset(&hint, 0, sizeof(hint)); + hint.ai_family = (r->family == 6 || r->family == 64)?AF_INET6:AF_INET; + if (getaddrinfo((char *)r->name, NULL, &hint, &ai)) { + if(r->family == 64 || r->family == 46){ + hint.ai_family = (r->family == 64)?AF_INET:AF_INET6; + if (getaddrinfo((char *)r->name, NULL, &hint, &ai)) return; + } + else return; + } + if(ai){ + if(ai->ai_addr->sa_family == AF_INET || ai->ai_addr->sa_family == AF_INET6){ + *SAFAMILY(r->sa)=ai->ai_addr->sa_family; + memcpy(SAADDR(r->sa), SAADDR(ai->ai_addr), SAADDRLEN(ai->ai_addr)); + r->res = *SAFAMILY(r->sa); + } + freeaddrinfo(ai); + } +} +#endif + uint32_t getip46m(int mode, int family, unsigned char *name, struct sockaddr *sa){ #ifndef NOIPV6 int detect; -#ifndef NOSTDRESOLVE - struct addrinfo *ai, hint; -#endif RESOLVFUNC tmpresolv; if(!sa) return 0; @@ -1210,23 +1245,16 @@ uint32_t getip46m(int mode, int family, unsigned char *name, struct sockaddr *s return 0; } #ifndef NOSTDRESOLVE - memset(&hint, 0, sizeof(hint)); - hint.ai_family = (family == 6 || family == 64)?AF_INET6:AF_INET; - if (getaddrinfo((char *)name, NULL, &hint, &ai)) { - if(family == 64 || family == 46){ - hint.ai_family = (family == 64)?AF_INET:AF_INET6; - if (getaddrinfo((char *)name, NULL, &hint, &ai)) return 0; - } - else return 0; - } - if(ai){ - if(ai->ai_addr->sa_family == AF_INET || ai->ai_addr->sa_family == AF_INET6){ - *SAFAMILY(sa)=ai->ai_addr->sa_family; - memcpy(SAADDR(sa), SAADDR(ai->ai_addr), SAADDRLEN(ai->ai_addr)); - freeaddrinfo(ai); - return *SAFAMILY(sa); - } - freeaddrinfo(ai); + { + struct stdresolve r; + + r.name = name; + r.family = family; + r.sa = sa; + /* a fiber's stack is not one to give it */ + if(onthreadstackfunc) (*onthreadstackfunc)(stdresolve46, &r); + else stdresolve46(&r); + return r.res; } #endif return 0; diff --git a/src/mainloop.c b/src/mainloop.c index a651017..881f84f 100644 --- a/src/mainloop.c +++ b/src/mainloop.c @@ -81,6 +81,9 @@ struct looper { unsigned nidle; struct sockreg **regs; /* by descriptor, the loop's thread only */ unsigned nregs; + /* what a fiber yielded to have run on the thread's stack */ + void (*stackcall)(void *arg); + void *stackarg; }; /* what srvspawn() posts to a loop */ @@ -141,8 +144,16 @@ static void fibergive(struct looper *lp, struct fiberslot *fs){ client is done. */ static void fiberrun(struct looper *lp, struct fiber *f){ struct fiberslot *fs = (struct fiberslot *)f; + int alive; - if(!fiberresume(f)) fibergive(lp, fs); + /* a call it asked for on the thread's stack made, and it goes on */ + while((alive = fiberresume(f)) && lp->stackcall){ + void (*call)(void *arg) = lp->stackcall; + + lp->stackcall = NULL; + call(lp->stackarg); + } + if(!alive) fibergive(lp, fs); /* in the middle of its client's code: started over from its stack's start for the next client, or freed where it cannot be */ else if(fs->abandoned){ @@ -1132,6 +1143,20 @@ static unsigned ncpus(void){ #endif } +/* onthreadstackfunc: func(arg) on the loop thread's own stack, for a fiber + calling what wants a big one: the loop waits as long as it takes. */ +static void onthreadstack(void (*func)(void *arg), void *arg){ + struct looper *lp = curlooper; + + if(!lp || !fibercurrent()){ + func(arg); + return; + } + lp->stackcall = func; + lp->stackarg = arg; + fiberyield(); +} + /* The loops, started the first time a client is to be served on one, for the life of the process: as many as loops said then. 0 if they run. */ static int loopsstart(void){ @@ -1191,6 +1216,7 @@ static int loopsstart(void){ #endif fiberpollfunc = fiberpoll; onloopfunc = onlooper; + onthreadstackfunc = onthreadstack; } _3proxy_mutex_unlock(&loops_mutex); return !i; diff --git a/src/proxy.h b/src/proxy.h index 64846e4..694a0d0 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -340,6 +340,7 @@ extern int (*fiberpollfunc)(struct pollfd *fds, unsigned nfds, int timeout); extern void (*fiberblockfunc)(SOCKET s, short events); extern void (*fiberclosefunc)(SOCKET s); extern int (*onloopfunc)(void); +extern void (*onthreadstackfunc)(void (*func)(void *arg), void *arg); int sockreadable(SOCKET s); #ifndef _WIN32 int fdclose(int fd);