on the semantics of connect(): EINTR, EALREADY, EINPROGRESS
From: David Madore (david.madore@ens.fr)
Date: 04/22/03
- Next message: joe@invalid.address: "Re: struct byte alignment on Linux"
- Previous message: Barry Margolin: "Re: struct byte alignment on Linux"
- Next in thread: joe@invalid.address: "Re: on the semantics of connect(): EINTR, EALREADY, EINPROGRESS"
- Reply: joe@invalid.address: "Re: on the semantics of connect(): EINTR, EALREADY, EINPROGRESS"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: david.madore@ens.fr (David Madore) Date: 22 Apr 2003 18:06:59 GMT
I have a question regarding the semantics of the Unix connect() system
call for blocking (stream) sockets and specifically what happens when
it is interrupted by a signal.
Question: assume fd is a file descriptor referring to a blocking,
SOCK_STREAM socket, say in the PF_INET protocol family. A process
attempts a first call to connect(fd,&name,namelen) and this call is
interrupted by a signal, returning EINTR. In what state is fd then
left? In particular, what happens if connect(fd,&name,namelen) is
immediately re-attempted? I can imagine several possible behaviors:
1. the second call returns immediately with EALREADY, as if the socket
were non-blocking,
2. the second call blocks until connection is achieved (in which case
success is reported) or another signal is received (in which case
EINTR is returned again), or, of course, an error is produced,
3. the second call retries the connection from start, as if the fd had
been closed and another socket had been produced,
4. the second call returns with EISCONN (sort of stupid, but you never
know), or
5. demons fly through your nose (i.e., undefined behavior), or again
6. this simply can't happen because connect() is never unterrupted by
a signal and never returns EINTR.
What does the Norm say (by "the Norm", I mean The Open Group's Single
Unix Specification, version 3, aka SUSv3)? If I read it to the
letter, it prescribes behavior 1 above: indeed, it says "If connect()
is interrupted by a signal that is caught while blocked waiting to
establish a connection, connect() shall fail and set errno to [EINTR],
but the connection request shall not be aborted, and the connection
shall be established asynchronously. [...] The connect() function
shall fail if: [...] [EALREADY] A connection request is already in
progress for the specified socket." There is nothing saying that
EALREADY is used only for non-blocking sockets, so the second call
should fail with EALREADY if the first one returned EINTR, starting an
asynchronous connection attempt: this is behavior 1 in my list above.
However, if we consider the spirit of the norm, then certainly
EINPROGRESS and EALREADY are reserved for non-blocking sockets: a
blocking socket, as its name indicates, should block until success
rather than return a "in progress" or "already in progress" error;
furthermore, it is the general idea that when a system call fails with
EINTR one shoud restart it with the same arguments. This is my
behavior 2. So the spirit of the norm prescribes behavior 2 above, in
my interpretation at least.
One thing is certain: the norm rules out behavior 6 above.
If I read correctly the Linux source code, and summary experiments I
have attempted seem to confirm this, it obeys behavior 2, in
accordance with the reasoning I have just outlined.
The FreeBSD source code, however, seems to think differently: I seem
to see that EALREADY would _never_ be returned by connect() under
FreeBSD, and that EISCONN would be returned when the connection is in
progress. Furthermore, it seems that when connect() is interrupted
and returns EINTR, the connection attempt is aborted, but I'm not sure
I decipher this correctly. Anyway, FreeBSD would have behavior 3 or 4
above, possibly 2, but certainly not 1.
The FreeBSD *man page*, on the other hand, does not document EINTR as
a possible return code for connect(), so that would imply behavior 6.
It documents EALREADY, but only for non-blocking sockets, excluding
behavior 1 again.
The OpenBSD source code, if I read it correctly, makes EALREADY
possible only for non-blocking sockets, again excluding behavior 1.
As for FreeBSD, I'm not sure whether it's 2, 3 or 4. The OpenBSD man
page documents EINTR as possible, so it's not 6, and also EALREADY as
possible only for non-blocking sockets, so 1 is excluded.
The Solaris man page (I don't have access to Solaris source code)
documents EALREADY as only possible for non-blocking sockets, so 1 is
excluded. And EINTR is reported as possible, though the meaning is
unclear, but 6 seems excluded.
This means a lot of confusion.
Does someone have an in-depth experience of what various actual Unixen
do when connect() receives a signal, and how a second connect() call
will react, and what is the right way of programming around this
(assuming non-blocking sockets are not an option)? Is there some way
to file a clarification request on the SUSv3 specification? Also,
what does POSIX say (if anything) in the matter?
Any information on the subject is welcome.
--
David A. Madore
(david.madore@ens.fr,
http://www.eleves.ens.fr:8080/home/madore/ )
- Next message: joe@invalid.address: "Re: struct byte alignment on Linux"
- Previous message: Barry Margolin: "Re: struct byte alignment on Linux"
- Next in thread: joe@invalid.address: "Re: on the semantics of connect(): EINTR, EALREADY, EINPROGRESS"
- Reply: joe@invalid.address: "Re: on the semantics of connect(): EINTR, EALREADY, EINPROGRESS"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
|