splice() pipes closed with fdclose(), the loop forgetting them first

sockmap waits on its splice() pipes as on its sockets, and on an event
loop a pipe waited on is registered there like a socket. They were
closed with close(), past the socket functions, so the loop kept the
registration of a closed descriptor, which epoll and kqueue drop on the
close: a socket given the same number later was taken for it, never
registered, and its fiber waited on it forever. fdclose() tells the
loop first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-25 12:54:46 +03:00
parent 7b873b80b1
commit 9cdac1cb60
4 changed files with 21 additions and 8 deletions

View File

@ -492,6 +492,16 @@ struct sockfuncs so = {
def_closesocket
};
#ifndef _WIN32
/* close() of a descriptor a fiber may have waited on, a pipe of splice():
its loop forgets it first, or a socket given the same number later would
be taken for it, and never registered. */
int fdclose(int fd){
if(fiberclosefunc) fiberclosefunc(fd);
return close(fd);
}
#endif
/* Whether s, found readable, is: data or its end there. A fiber's wait
may answer with what a socket was reported ready for, until an
operation finds it not to be: whatever takes readability for an end,

View File

@ -393,10 +393,10 @@ void releaseparam(struct clientparam * param) {
param->clibuf = param->clibufdata;
param->srvbuf = param->srvbufdata;
#ifdef WITHSPLICE
if(param->pipecli[0] >= 0) close(param->pipecli[0]);
if(param->pipecli[1] >= 0) close(param->pipecli[1]);
if(param->pipesrv[0] >= 0) close(param->pipesrv[0]);
if(param->pipesrv[1] >= 0) close(param->pipesrv[1]);
if(param->pipecli[0] >= 0) fdclose(param->pipecli[0]);
if(param->pipecli[1] >= 0) fdclose(param->pipecli[1]);
if(param->pipesrv[0] >= 0) fdclose(param->pipesrv[0]);
if(param->pipesrv[1] >= 0) fdclose(param->pipesrv[1]);
param->pipecli[0] = param->pipecli[1] = param->pipesrv[0] = param->pipesrv[1] = -1;
#endif
/* Not shut down on Windows: there shutting down receiving resets the

View File

@ -337,6 +337,9 @@ extern int (*fiberpollfunc)(struct pollfd *fds, unsigned nfds, int timeout);
extern void (*fiberblockfunc)(SOCKET s, short events);
extern void (*fiberclosefunc)(SOCKET s);
int sockreadable(SOCKET s);
#ifndef _WIN32
int fdclose(int fd);
#endif
void nonblock(SOCKET sock);
int sysresolves(int mode);
int acceptagain(void);

View File

@ -1009,13 +1009,13 @@ CLEANRET:
/* 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 */
if(inclientpipe && pipecli[0] >= 0){
close(pipecli[0]);
close(pipecli[1]);
fdclose(pipecli[0]);
fdclose(pipecli[1]);
pipecli[0] = pipecli[1] = -1;
}
if(inserverpipe && pipesrv[0] >= 0){
close(pipesrv[0]);
close(pipesrv[1]);
fdclose(pipesrv[0]);
fdclose(pipesrv[1]);
pipesrv[0] = pipesrv[1] = -1;
}
#endif