meaning of EPOLLERR from epoll_wait with a pipe - linux

This is similar to the question 1 but is about pipes. If epoll_wait returns EPOLLERR for the write end of a pipe, how do I distinguish a general error condition from the read end of the pipe been closed? For the sockets case the answer was to use "use getsockopt and SO_ERROR to get the pending error" and compare that with EPIPE. But what API should I use for the pipe as the pipe created by pipe(2) on Linux or returned by opening a named pipe is not a socket?

Other than the other end being closed, there aren't any errors on a write end of a pipe which could be detected by epoll. The few errors that are possible on an actual call to write() could not be known in advance (e.g., if you pass an invalid buffer pointer), so epoll cannot detect them. Therefore, if epoll tells you there's an error, it is EPIPE.
(OK, there is actually another possible error condition, but it can be triggered only by a programming error: if you close the file descriptor and then use epoll_wait while it is on the list - I don't know how epoll will react to that).

Related

Semantically, why does epoll_wait sends me EPOLLIN, after read end had been shut down?

The question is straightforward. The shutdown(..., SHUT_RD) was called on the socket after read has returned zero. The epoll_wait keeps sending me EPOLLIN event for the socket.
I know I can, and must do, in my situation, call epoll_ctl to modify the event mask. Naive thoughts lead me to think that, if close removes record from the epoll list, then shutdown had to mark appropriate socket end.
The question is, semantically, why epoll_wait do report me with read available, given that read returned 0 socket were shut down for reading?
epoll_wait() will report EPOLLIN if trying to read from the socket would return immediately rather than blocking.
Once you do shutdown(..., SHUT_RD); all calls to read() return 0 immediately. Since it won't block, epoll_wait() reports that the socket is readable.

Is it possible for UnixStream::send to return EWOULDBLOCK?

I am using UnixStream and I am not calling set_nonblocking(true). I thought a blocking socket would never return EWOULDBLOCK; is that true?
can a blocking socket return EWOULDBLOCK
It depends on the OS implementation, but NO for POSIX-compliant OSs and Linux.
From recv() of POSIX.1-2017:
[EAGAIN] or [EWOULDBLOCK]
The socket's file descriptor is marked O_NONBLOCK and no data is
waiting to be received; or MSG_OOB is set and no out-of-band data is
available and either the socket's file descriptor is marked O_NONBLOCK
or the socket does not support blocking to await out-of-band data.
Linux is not POSIX-certified. But it's still NO. From recv(2):
EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received. POSIX.1 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
The general answer is no, however I've seen two exceptions:
If you set a timeout on the socket with SO_RCVTIMEO or SO_SNDTIMEO - in this case, a receive or send will return with EWOULDBLOCK if the timeout elapses while no input data becomes available or the output buffer remains full, respectively.
If you call send/recv with the MSG_DONTWAIT flag, effectively turning the socket into a nonblocking one temporarily.
I don't know if UnixStream actually exposes the above functionalities, this is a POSIX question not a Rust one, and I'm not familiar with Rust.

What Error Shall We handle in Using Sockets

When using non-blocking sockets to communicate with clients, what error codes we must care and do something other than calling close() directly? Can anyone list them and give a few comments about what extra work we must do?
Currently we only handle the EAGAIN and EWOULDBLOCK, for all the other errors we just close the socket. Is this kind of socket exception routine enough for a server software?
I think you've mostly got it right. There are two other transient errors that my code retries on rather than closing the socket, they are:
EINTR - this can be returned if the system call was interrupted by delivery of a signal. The usual appropriate response is to try the system call again. (See here for details on why this can occur)
ENOBUFS - Under certain circumstances, send() can return this if the network interface's output queue is full. (yes, you'd expect send() to return EWOULDBLOCK or EAGAIN instead, but I have seen this returned)

how to know whether the remote end closed the socket using send system call?

the send() system call succeeds for the first time even if the remote end closed the connection.
If i send again, then sigpipe will be generated.
I don't want to use recv system call to know whether remote end closed the connection.
So can someone tell me how can i detect whether the remote end closed the connection using only send system call ?
The first thing you should do is call signal(SIGPIPE, SIG_IGN); to avoid receiving SIGPIPE on send() or, on Linux, pass MSG_NOSIGNAL flag to send(). Having done that, send() will not generate SIGPIPE signal when a peer has disconnected, but rather return -1 with errno == EPIPE.
Sending SIGPIPE is historical behaviour of write() function. http://pubs.opengroup.org/onlinepubs/009604599/functions/write.html
EPIPE An attempt is made to write to a pipe or FIFO that is not open for reading by any process, or that only has one end open. A SIGPIPE signal shall also be sent to the thread.
Originally it was created for shell pipes (like cmd | other_cmd). When ctrl-c is pressed during shell pipe execution only the last process in the pipe receives SIGINT and terminates. To terminate other processes in the pipe SIGPIPE is sent to the process trying to write() into STDOUT whose reader has already terminated.

Client connections with epoll

I'm programming an application(client/server) in C++ for linux using epoll y pthreads but I don't know how to handle the connect() calls for attach a new connection in the descriptor list if a loop with epoll_wait() is running(Edge-triggered), How to can I do it?... I could to use a dummy file descriptor to trigger an event and scape of wait?, or a simple call to connect() could fire the event??...
Sorry for my bad english...
Yes, you can use another file descriptor that's just for waking up your epoll_wait() loop. Use pipe() to create the file descriptor. Add the reading end of the pipe to your epoll list, and write a single byte to the writing end when you want to wake it up. The reading side can just read that byte and discard it.

Resources