> Hi!
>
> * David Schwartz wrote on Tue, Aug 28, 2007 at 08:56 -0700:
> > > I think it is important to note that a blocking read usually
> > > should return if one single byte is available (even if more had
> > > been requested)
> >
> > Correct.
> >
> > > and a blocking write should return as soon as at
> > > least one byte has been written.
> >
> > No. A blocking write should block until all the requested data cen be
> > written.
>
> ahh, interesting. Why should it?

Because this is what most people want it to do. If you tried to write two
bytes, why would you want to wait until the first one could be written but
not wait until the second one could be written? It just doesn't make much
sense.

> here, some (ancient) man 2 write tells:
>
>   write  writes  up to count bytes to the file referenced by the
>   file descriptor fd from the buffer  starting  at  buf.
>
> also, there is:
>
>   POSIX  requires that a read() which can be proved to occur
>   after a write() has returned returns the new  data.   Note that
>   not all file systems are POSIX conforming.
>
> but I think, does not require that write needs to block as long,
> because an efficient implementation could queue the reset of the
> data (e.g. if an NFS server is slow or a serial line not ready),
> return in write, `remembering' this when read on this fd is
> called and before internally attempting the read functionality,
> it could empty the queue. I consider this comfortable for the
> calling code (I have some simple buffering protocol
> implementations doing something like this).
>
> So wouldn't it be correct to implement a write that
> systematically writes one single byte per call only (correct even
> if unperformant of course)?

Yes, it would be correct. It just would be sub-optimal.

There is some code out there that assumes that a blocking write will fully
complete unless there is an error. This code is, as you point out, broken.

However, a 'read' that blocked after some data was received would be broken.

> Are signals and EINTR also of concern? I think in practice it
> makes things not easier...

Most implementations will return a short write under some circumstances.
Interruption is definitely one of them.

> > > For buffered communications, I assume, this should always be
> > > possible. If there is data stored in some internal buffer (making
> > > select returning `ready for reading'), this data must be readable
> > > or an error must be returned. I would consider the loss of the
> > > data from such an internal buffer an error which should be
> > > reported (instead of blocking). `ready for writing' in such a
> > > subsystem would mean that some write buffer has at least one byte
> > > free and thus the next write call can return (without blocking).
> >
> > You are thinking 'TCP' while we are talking about the semantics
> > for 'select'. Think about UDP. Is there anything in the
> > standard that prohibits an implementation under memory pressure
> > from just discarding a UDP receive queue?

> Yes, there are the select usage semantics :-)
> Discarding a UDP receive queue after a select returned that data
> is inside could lead to a blocking read, I see. I think, it does
> not matter much here, that UDP `allows' queue discarding because
> it is not reliable, because as you said select is protocol
> independent. So I think, if select tells (according to my ancient
> man page) `to see if  a read  will not block' (<-- citation), a
> correct implementation needs to guarantee that. If queue
> discarding is possible, a flag must be stored (or so) to make
> read return EAGAIN or whatever (probably causing most code to
> break anyway, because noone expects EAGAIN after select > 0 :-)).

That's simply impossible to do. The problem is that there is no unambiguous
way to figure out whether an operation is the one that's not supposed to
block. Consider:

1) A thread calls 'select'.

2) That thread later calls 'read'.

If the 'select' changes the semantics of the 'read', then if the thread
didn't know that some other code called 'select' earlier, the later
read-calling code can break. Sometimes people call 'select' just for
statistical purposes and don't check the descriptor just because of what
'select' returned.

> Of course, it probably isn't the best idea to rely on that, maybe
> some embedded highly size optimised lib makes the one or other
> compromise or so... :)

There are known implementations that perform some data integrity checks at
'recv' time, so a 'select' hit that results in the data being dropped later
can lead to 'recvmsg' blocking. You can argue that these implementations are
deficient, but I think that argument would be inconsistent. This behavior is
accepted with 'accept', and it's precisely the same issue.

> The statement `to see if  a read  will not block' does not sound
> very concrete or formally. For instance, only the next read can be in
> scope and probably only if no other call (recv, write, don't
> know) is performed on this fd - I guess.

The problem is that it becomes very hard to figure out what an "other call"
is. What about 'setsockopt'? If you don't even know what you're asking for,
you wouldn't even know if you had it. ;)

> > Now, think TCP for a second. Suppose a system received some TCP
> > data but delayed the ACK. It unblocks 'select', but later comes
> > under extreme memory pressure, so it discards the
> > unacknowledged data. Now, as far as I know, no implementation
> > does this. But nothing in the standard says it cannot do this
> > if it wants to.

> Yes, and additionally, there may be implementations supporting a
> select function but at the same time not even conforming the
> standard, I think such `TCP stacks' exist.
> BTW, which standard would it be, `4.4BSD'?

I'm talking about The Single Unix Specification or The Open Group Base
Specification.
http://www.opengroup.org/onlinepubs/009695399/functions/FD_SET.html
This is reasonably clear that 'select' reports current status, just as
functions like 'stat' do. They provide no more of a future guarantee than
functions like 'stat' do.

> > > If it cannot be written for any reason I think a nice
> > > implementation should return an error in this case.

> > Unfortunately, that's just not possible. The problem is that this would
> > require figuring out which socket operations are the subsequent
> > operations
> > that you think should not block. This can't be done reliably, and will
> > occasionally break code that works fine now.

> mmm... If the man page tells that `a read will not block' (which,
> BTW, is also told for write, but not for accept), I think the
> possible subsequent operations are defined: read or write.

Why include "write" in the claim that a read will not block? What about
"setsockopt"? In any event, only Windows has documentation that uses that
kind of language. I think it was just sloppiness, as the select function on
Windows was a hack job just to support compatability with existing Berkeley
sockets implementations.

> > > I understand select as a call to (more or less) report the state
> > > of some internal communication buffers and expect to make
> > > guarantees about those buffers (no concurrent threads etc of
> > > course).
> >
> > The implementation simply cannot predict the future.

> Do we speak about future `broken' implemenatations? If taking
> implementation bugs into account, of course /nothing/ could ever
> be guaranteed. Maybe even the select blocks in kernel 2.8.1241
> because of a bug, sure :)

These are not implementation bugs. These are implementation quirks. They
don't violate the relevant standards, so your code has to tolerate them if
it claims compliance with those standards.

> > A file is always ready. There is never anything to wait for.

> I disagree here. Files may not be ready, because NFS Server may
> not be responsing, a USB stick may be slow, a FTP file system may
> need to dialup an ISDN line, the FIFO or STDIN could be empty,
> the harddisk may be busy and need some milliseconds to read in
> the requested blocks --- or the file could be a device or a
> socket :)

Nevertheless, it's ready and cannot be waited for. With slow file systems,
they generally try to perfectly mimic the semantics of fast file systems. An
empty file is still ready to be read right now, to report correctly that it
is empty.

> > > David, do you mean `it cannot be guaranteed because no
> > > implementation can be guaranteed to be 100% correct and may fail
> > > in a complex situation' or do you mean `it cannot be guaranteed
> > > logically/theoretically, even if the implementation is assumed to
> > > be 100% correct, because of a logical dilemma'?
> >
> > One could theoretically make an implementation that did 100%
> > guarantee it.  But it would only make code that's broken appear
> > to work and it would break some code that currently works.
> > Consider any current program that calls 'select' just as a
> > status-reporting function and then expects a subsequent send to
> > block until all the data can be sent.

> mmm... beside I'd consider a program expecting write to block
> until all data has been written already broken, I see your point,
> ok... So you say that theoretically it is guaranteed that read
> won't block after select but practically there will always be a
> risk to have an implementation not fulfilling this guarantee,
> right?

No, it's not guranteed even theoretically. The 'select' function is simply a
status-reporting function that does not and cannot guarantee what will
happen in the future. Assuming it will is as serious a bug as checking
permissions with something like 'access' and then assuming the information
must still be valid in the future.

DS


______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@openssl.org
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to