Also see this question, unanswered as of now.
There is a lot of confusion about EPOLLHUP
, even in the man
and Kernel docs. People seem to belie
For these kind of questions, use the source! Among other interesting comments, there is this text:
EPOLLHUP
is UNMASKABLE event (...). It means that after we receivedEOF
,poll
always returns immediately, making impossiblepoll()
onwrite()
in stateCLOSE_WAIT
. One solution is evident --- to setEPOLLHUP
if and only ifshutdown
has been made in both directions.
And then the only code that sets EPOLLHUP
:
if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)
mask |= EPOLLHUP;
Being SHUTDOWN_MASK
equal to RCV_SHUTDOWN |SEND_SHUTDOWN
.
TL; DR; You are right, this flag is only sent when the shutdown has been both for read and write (I reckon that the peer shutdowning the write equals to my shutdowning the read). Or when the connection is closed, of course.
UPDATE: From reading the source code with more detail, these are my conclusions.
About shutdown
:
shutdown(SHUT_WR)
sends a FIN
and marks the socket with SEND_SHUTDOWN
.shutdown(SHUT_RD)
sends nothing and marks the socket with RCV_SHUTDOWN
.FIN
marks the socket with RCV_SHUTDOWN
.And about epoll
:
SEND_SHUTDOWN
and RCV_SHUTDOWN
, poll
will return EPOLLHUP
.RCV_SHUTDOWN
, poll
will return EPOLLRDHUP
.So the HUP
events can be read as:
EPOLLRDHUP
: you have received FIN
or you have called shutdown(SHUT_RD)
. In any case your reading half-socket is hung, that is, you will read no more data.EPOLLHUP
: you have both half-sockets hung. The reading half-socket is just like the previous point, For the sending half-socket you did something like shutdown(SHUT_WR)
.To complete a a graceful shutdown I would do:
shutdown(SHUT_WR)
to send a FIN
and mark the end of sending data.EPOLLRDHUP
.PS: About your comment:
it's counterintuitive to get writable, as the writing half is closed
It is actually expected if you understand the output of epoll
not as ready but as will not block. That is, if you get EPOLLOUT
you have the guarantee that calling write()
will not block. And certainly, after shutdown(SHUT_WR)
, write()
will return immediately.