[fpc-pascal] TStream.ReadBuffer doesn't try hard enough to read the requested bytes

Michalis Kamburelis michalis.kambi at gmail.com
Sun Jun 16 03:18:13 CEST 2013


Ewald wrote:
> And what with non-blocking pipes pipes? Wait for a *some* period
> until you get all data? It is up to the programmer to do this INHO.

If you want to get partial data (instead of waiting until all requested 
data is available) you should just use TStream.Read instead of 
TStream.ReadBuffer. Then it's indeed up to the programmer to handle this.

All existing usage of ReadBuffer right now works only if you get all the 
data in a single Read call (which translates to a single read syscall on 
Linux, as far as I see), otherwise it fails with exception. That's just 
wrong behavior for a ReadBuffer IMHO.

> Also, if you would enforce this behaviour on all kinds of streams,
> you might (`will`, actually) get unexpected performance drops...

I don't see why you would get any performance drop. If you're lucky and 
a single Read call is all you need, then the speed is the same as 
current implementation, since the 1st check "if Read(Buffer,Count)<Count 
then" will return false and we will not call Read again.

Besides, we're talking about correctness here. And TStream.ReadBuffer is 
non-virtual and must be suitable for all TStream descedants, handling 
whatever virtual TStream.Read does.

>
>>
>> For example: on Linux, TFileStream.Read calls fpRead which is a
>> system call, and it definitely can return less than requested
>> amount of bytes, and it doesn't mean that stream ended. (See e.g.
>> Libc docs:
>> http://www.gnu.org/software/libc/manual/html_mono/libc.html#I_002fO-Primitives
>> : "If read returns at least one character, there is no way you can
>> tell whether end-of-file was reached. But if you did reach the end,
>> the next read will return zero. ") So if you use TFileStream with
>> ReadBuffer, your code is working purely by accident right now...
>>
>
> But if you work with a blocking fd (which, for example, a TFileStream
> uses IIRC) you will always get your data. If there is more data to be
> read(), but it would take time, it simply takes time. The only case
> where zero is returned, is IIRC (like you quoted) when the end of the
> `fd` (e.g. pipe, socket, file) is reached on *blocking
> filedescriptors*.

Yes, zero is returned only if stream ends. But a result that is 
non-zero, but still less than requested Count, doesn't tell you if the 
stream ends. The whole point is whether ReadBuffer should take this into 
account.

> FYI: I've never had the problem of a `partial read` (e.g.
> SomeStream.Read(50) returning 36 or something) on linux, osx and
> windows; so perhaps you have exposed some bug in an obscure TStream
> descendant?
>

Reproducing this behavior proved to be difficult, but I definitely 
observed it with TFileStream on Linux.

And, even if it would accidentally work for some streams, that's not a 
solution. Like I mentioned, TStream.ReadBuffer is non-virtual and must 
be suitable for every TStream.

Michalis



More information about the fpc-pascal mailing list