Closed SUPERCILEX closed 7 months ago
It is more likely documented in close(2), as this is where the issue lies, although this page was written before the concept of io_uring.
The section about " Multithreaded processes and close()" hints at it
On Linux (and possibly some other systems), the behavior is
different: the blocking I/O system call holds a reference to the
underlying open file description, and this reference keeps the
description open until the I/O system call completes. (See
[open(2)](https://man7.org/linux/man-pages/man2/open.2.html) for a discussion of open file descriptions.) Thus, the
blocking system call in the first thread may successfully
complete after the close() in the second thread.
Since the fd is ref counted, you may want to consider using shutdown(2) before close(2).
https://man7.org/linux/man-pages/man2/shutdown.2.html:
SYNOPSIS
#include <sys/socket.h>
int shutdown(int sockfd, int how);
DESCRIPTION
The shutdown() call causes all or part of a full-duplex
connection on the socket associated with sockfd to be shut down.
If how is SHUT_RD, further receptions will be disallowed. If how
is SHUT_WR, further transmissions will be disallowed. If how is
SHUT_RDWR, further receptions and transmissions will be
disallowed.
Fascinating, I had no idea this was a kernel wide thing. This issue is probably good enough documentation then.
Thanks for the shutdown tip! For folks wondering, that will automatically cancel the multi recv and fixes a bug where clients stay blocked on a recvmsg even though the server closed the connection.
As far as I can tell, close doesn't seem to do anything at all? If the client connection is still open, I'll continue to receive messages even after the close syscall successfully returns. This is extremely surprising behavior and I couldn't find any docs mentioning this.
It seems like the only way to correctly close a client connection is to first perform an async cancellation (which errors out the recv) and then do the close.
I can see how being able to immediately close the fd would be useful, so I'm guessing this is expected behavior, but it should definitely be documented somewhere as it's pretty confusing to debug.