Relays: buffers let go while spliced on a connection's last mapping

sockmap() of fibers and threads kept both buffers for as long as it
mapped, even with a direction spliced. On a connection's last mapping
(maptail set), as the callbacks already do, a direction spliced lets its
empty buffer go: bufdrop(), shared with the callbacks now. It is made
again before a read into it, back to copying or draining what TLS held,
and both are there again once sockmap() returns, for whatever comes
after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-27 18:37:52 +03:00
parent 1201031404
commit cbc7245925
4 changed files with 28 additions and 7 deletions

View File

@ -497,13 +497,7 @@ static void cbbuf(struct clientparam *param, int side, unsigned char **buf, unsi
relay can use it: the client's fiber gave up its stack. The next read relay can use it: the client's fiber gave up its stack. The next read
makes it again. */ makes it again. */
static void cbbufdrop(struct cbmap *m, int side){ static void cbbufdrop(struct cbmap *m, int side){
struct clientparam *param = m->param; if(m->abandoned) bufdrop(m->param, side? SERVER : CLIENT);
unsigned char **own = side? &param->srvbufdata : &param->clibufdata;
unsigned char **buf = side? &param->srvbuf : &param->clibuf;
if(!m->abandoned || !*own || *buf != *own || (side? param->srvinbuf : param->cliinbuf)) return;
free(*own);
*own = *buf = NULL;
} }
/* what a way has buffered and in its pipe: none once it is not moving */ /* what a way has buffered and in its pipe: none once it is not moving */

View File

@ -361,6 +361,7 @@ int paramstr(unsigned char *field, size_t size, const unsigned char *s);
#define PARAMSTR(p, f, s) paramstr((p)->f, sizeof((p)->f), (const unsigned char *)(s)) #define PARAMSTR(p, f, s) paramstr((p)->f, sizeof((p)->f), (const unsigned char *)(s))
void bufrelease(struct clientparam * param, DIRECTION which); void bufrelease(struct clientparam * param, DIRECTION which);
int bufget(struct clientparam * param, DIRECTION which); int bufget(struct clientparam * param, DIRECTION which);
void bufdrop(struct clientparam * param, DIRECTION which);
int bufalloc(struct clientparam * param); int bufalloc(struct clientparam * param);
void buffree(struct clientparam * param); void buffree(struct clientparam * param);

View File

@ -102,6 +102,20 @@ void buffree(struct clientparam * param){
param->clibufdata = param->srvbufdata = NULL; param->clibufdata = param->srvbufdata = NULL;
} }
/* An empty own buffer let go, while the connection's last mapping has
no use for it, its direction spliced; bufget() makes it again. */
void bufdrop(struct clientparam * param, DIRECTION which){
unsigned char **own = which? &param->srvbufdata : &param->clibufdata;
unsigned char **buf = which? &param->srvbuf : &param->clibuf;
unsigned *in = which? &param->srvinbuf : &param->cliinbuf;
unsigned *off = which? &param->srvoffset : &param->clioffset;
if(!*own || *buf != *own || *in != *off) return;
free(*own);
*own = *buf = NULL;
*in = *off = 0;
}
/* Starts an empty buffer over from its beginning, and goes back to the /* Starts an empty buffer over from its beginning, and goes back to the
connection's own buffer if a filter needed a bigger one. Does nothing connection's own buffer if a filter needed a bigger one. Does nothing
while anything is left unread. */ while anything is left unread. */

View File

@ -378,6 +378,12 @@ log("done send to client from buf");
splicesrv = wantbufsrv = streaksrv = 0; splicesrv = wantbufsrv = streaksrv = 0;
if(param->srvinbuf < SRVROOM) TOSERVERBUF = 1; if(param->srvinbuf < SRVROOM) TOSERVERBUF = 1;
} }
/* the connection's last mapping: a direction spliced has no use for
its buffer while it is empty */
if(param->maptail){
if(splicecli && !wantbufcli && !draincli && !inclientbuf) bufdrop(param, CLIENT);
if(splicesrv && !wantbufsrv && !drainsrv && !inserverbuf) bufdrop(param, SERVER);
}
{ {
/* each direction goes through its pipe if spliced, through its /* each direction goes through its pipe if spliced, through its
buffer otherwise; what was in the buffer before is sent first */ buffer otherwise; what was in the buffer before is sent first */
@ -597,6 +603,8 @@ log("done read from server to pipe\n");
#ifdef WITHLOG #ifdef WITHLOG
log("read from client to buf"); log("read from client to buf");
#endif #endif
/* let go while spliced: made again */
if(!param->clibuf && bufget(param, CLIENT)) RETURN(21);
/* no address: it would overwrite the client's with what a /* no address: it would overwrite the client's with what a
stream socket gives, an empty one for a Unix socket */ stream socket gives, an empty one for a Unix socket */
res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)CLIROOM - param->cliinbuf, fromclient-inclientbuf), 0, NULL, NULL); res = param->srv->so._recvfrom(param->sostate, param->clisock, (char *)param->clibuf + param->cliinbuf, (int)MIN((uint64_t)CLIROOM - param->cliinbuf, fromclient-inclientbuf), 0, NULL, NULL);
@ -654,6 +662,7 @@ log("done read from client to buf");
#ifdef WITHLOG #ifdef WITHLOG
log("read from server to buf"); log("read from server to buf");
#endif #endif
if(!param->srvbuf && bufget(param, SERVER)) RETURN(21);
res = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)param->srvbuf + param->srvinbuf, (int)MIN((uint64_t)SRVROOM - param->srvinbuf, fromserver-inserverbuf), 0, NULL, NULL); res = param->srv->so._recvfrom(param->sostate, param->remsock, (char *)param->srvbuf + param->srvinbuf, (int)MIN((uint64_t)SRVROOM - param->srvinbuf, fromserver-inserverbuf), 0, NULL, NULL);
if(res <= 0) { if(res <= 0) {
FROMSERVER = 0; FROMSERVER = 0;
@ -1009,6 +1018,9 @@ log("timeout");
CLEANRET: CLEANRET:
/* what comes after a mapping finds the buffers there */
if(((!param->clibuf && bufget(param, CLIENT)) || (!param->srvbuf && bufget(param, SERVER))) && !res) res = 21;
#ifdef WITHSPLICE #ifdef WITHSPLICE
/* empty pipes stay with the connection for its next mapping; one still /* empty pipes stay with the connection for its next mapping; one still
holding data, left by an error or a timeout, cannot be used again */ holding data, left by an error or a timeout, cannot be used again */