views:

7319

answers:

4

According to the java api, the InputStream.read() is described as:

If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.

I have a while(true) loop doing a read and I always get -1 when nothing's sent over the stream. That's expected.

My question is when would read() ever block? Since if it doesn't get any data it returns -1. I would expect a blocking read to wait until data is received. If you've reached the end of the input stream, shouldn't read() simply wait for data instead of returning -1?

Or does read() only block if there's another thread accessing the stream and your read() cannot access the stream?


Which leads me to my next question. I used to have event listener (provided by my library) that would notify me when data is available. When I was notified I would call while((aByte = read()) > -1) store the byte. I was puzzled when I'd get TWO events in very close time proximity and not all my data was being displayed. It seemed like only the tail end of the second event's data would be displayed and the the rest was missing.

I eventually changed my code so that when I get an event I'd called if(inputStream.available() > 0) while((aByte = read()) > -1) store the byte. Now it worked properly and all my data was displayed.

Can someone explain this behavior? The InputStream.available() is said to return the number of bytes you can read before blocking the next caller (of the stream?). Even if I don't use .available() I would expect the read of the first event to just block the read of the second event, but not erase or consume too much stream data. Why would doing this cause not all of my data to be displayed?

+6  A: 

It returns -1 if it's end of stream. If stream is still open (i.e. socket connection) but no data has reached the reading side (server is slow, networks is slow,...) the read() blocks.

You don't need call available(). I have a hard time understanding your notification design, but you don't need any calls except read() itself. Method available() is there for convenience only.

Vladimir Dyuzhev
it is strange that I require available() to be there or else I don't get all my data. Can you explain why I need available()?
jbu
(available isn't even guaranteed to return anyting by 0.)
Tom Hawtin - tackline
you don't need available(). your code needs a fix.
Vladimir Dyuzhev
+11  A: 

A read of -1 isn't doesn't mean, "there's no data present on this attempt," it means, "the stream is closed and there will never be any data so stop asking."

If the end of the stream has not been reached, a read() call will block, just as the documentation specifies.


With regard to your second question, it sounds like you may have had a race condition. Without seeing the code in question, I'm guessing that the problem actually lay in your method of "display". Perhaps the attempt to display by the second notification was somehow clobbering the work done during the first notification.


I've only used javax.comm for serial port I/O, and it implements InputStream as intended. It's possible that the rxtx implementation violates the contract of InputStream.read(), but if so, that's a very bad thing. Any code that was written to work with InputStream in general relies on the fact that -1 means EOF. Passing a broken java.io.InputStream to such code will cause problems. If rxtx supports non-blocking I/O, they should have created their own interface for reading, not extended InputStream.

erickson
I am using the inputstream for a COM port. Again, if I do a while loop of a read, it will always return -1 until the device (the other end of the COM Port) outputs data. I never have to reopen the stream.So your explanation doesn't make sense to me. Is the device reopening the stream every time?
jbu
What library are you using to read the port?
erickson
rxtx ... a java library to connect to serial and parallel ports
jbu
Hmm, I've only used javax.comm, which implements InputStream as intended. It's possible that the rxtx implementation violates the contract of InputStream.read().
erickson
maybe it's the device. The device is using a COM/Serial port wrapped with a USB interface. It's a strange interface.
jbu
and no, I don't think the display was the problem. I am simply outputing the stream's data over the system.out stream.
jbu
So your "while" loop was "while ((abyte = in.read()) != -1) System.out.write(abyte);"? No intermediate buffers, or references to member variables in the enclosing object?
erickson
there is an intermediate buffer
jbu
That is true, that many serial communication libraries let you open the stream in a non-blocking mode, meaning that read() returns immediately, either with an octet or -1 in case of... well... EOF, or "no data" condition.It is also true, that InputStream documentation writes about returning -1 *only* on EOF.What is missing in InputStream docs is the definition of EOF condition. Most people assume that once EOF is reached, only -1 will be returned by read(). But not all assume so, and it leads to unspecified behaviours when working with non-blocking libs.
pwes
Just as a note, in Perl there are also some special cases when eof function can return -1 and later on more valid data. See http://perldoc.perl.org/functions/eof.html for details.
pwes
@pwes - that's true, but in this particular case, the `InputStream` class establishes a specific contract that *should* be upheld by *every* implementation. This allows callers to treat all `InputStream` implementations alike, without special treatment for one implementation that violates the contract. It's perfectly valid for a non-blocking implementation of an input stream to use a -1 value to signal "no data", but it should declare a new interface, not try to masquerade as an `InputStream`.
erickson
@erickson, I totally agree with you about keeping the contract; but what is unsaid in API is the definition of EOF -- this ambiguity leads to this InputStream contract "violation". Clear API: yes, silent assumntions: no!
pwes
I don't understand. The API defines end-of-stream as -1 and specifies that the call will block until data is available. What is ambiguous?
erickson
+5  A: 

OK, this is a bit of a mess so first thing lets clear this up: InputStream.read() blocking has nothing to do with multi-threading. If you have multiple threads reading from the same input stream and you trigger two events very close to each other - where each thread is trying to consume an event then you'd get corruption: the first thread to read will get some bytes (possibly all the bytes) and when the second thread gets scheduled it will read the rest of the bytes. If you plan to use a single IO stream in more then one thread, always synchornized on some external constraint.

Second, if you can read from your InputStream until you get -1 and then wait and can read again later, then the InputStream implementation you are using is broken! The contract for InputStream clearly states that an InputStream.read() should only return -1 when there is no more data to read because the end of the entire stream as been reached and no more data will EVER be available - like when you read from a file and you reach the end.

The behavior for "no more data is available now, please wait and you'll get more" is for read() to block and not return until there is some data available (or an exception is thrown).

Guss
Good explanation. I am starting to believe the inputstream implementation I am using IS broken.
jbu
+3  A: 
this is some great info, thanks!
jbu