[Rxtx] flush twice, it is a long way to the kitchen
Gregg Wonderly
gergg at cox.net
Wed Sep 27 09:10:09 MDT 2006
Dr. Douglas Lyon wrote:
> This last bit, a closed stream cannot be reopened is really telling.
> The roles of "flush" and "close" are often combined in an implementation,
> implicitly. They are also often (but not always) left out of the specification!
In the Unix I/O implementation, there is no flush call. Only write(2) and
close(2) are documented to work for any particular fd returned from open(2).
So, historically, close(2) has always had to include a wait for buffers to
empty. In the early days of various Unix implementations, when every terminal
was connected via a serial port, new serial hardware had new bugs. Close was
always a problem. When we used modems to do UUCP, kermit etc, we always had to
make sure and use appropriate ioctl(2) settings to manage the recognition of
flow control at particular moments so as to avoid certain bugs and situations
arrising from broken hardware and software.
> I would say that is not an optimal treatment and that we can do better.
>
> For example:
> http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#close()
...
> There is no flush in the example! Technically, according to the spec,
> this should write out NOTHING! But it DOES WORK!! Why?
As noted above, this, historically has had to work because there is not always
an equivalent flush() at the OS level.
> But from the source code of the ObjectOutputStream.java, I see:
> public void close() throws IOException {
> flush();
> clear();
> bout.close();
> }
> So, the flush is not in the specification, but it is in the implementation.
The buffering mechanism available in Java are a design paradigm meant to allow
functionality similar to what stdio(3) provides to the Unix programmer. The
issue is that various I/O streams are bi-directional, and it is necessary then
to flush the output before reading more input, in order for these conversational
exchanges to work. In raw Unix/C I/O with read(2) and write(2), there is an
implicit flush, because there is no other mechanism available. For certain
types of devices, there are ioctl() operations to do line mode buffering. For
some types of I/O, such as to a Unix PIPE, you are dealing with a one-way based
I/O subsystem that may perform buffering in particular circumstances. This is
one of the reasons why Berkeley Unix folks created Unix-Domain Sockets. They
allow conversational I/O to occur between processes without interfering buffering.
> As a Java programmer, I have gotten used to making the assumption
> that flush and close are combined and that the flush is implicit in
> the close.
>
> I am now starting to question my assumption. Whatever we decide,
> it should probably be explicit in the specification.
Don't question your assumption. close() must include flush(), in Java to be
correct. There is no ifs, ands, or buts about it.
> There is no consistency on close implementations in the IO package.
> Check out the close on the ByteArrayOutputStream:
> /**
> * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
> * this class can be called after the stream has been closed without
> * generating an <tt>IOException</tt>.
> * <p>
> *
> */
> public void close() throws IOException {
> }
This is a bad example, because the write() operation extends the array
automatically, and makes the implementation meet the requirements all the APIs
represented in the class. If write did not extend the array, the toByteArray()
and close() would have to do that eventually. The implementation is less
complex because it does the work early rather than defering it to later and thus
requiring a check to see if the flush activity had been done.
> Now check the FilterOutputStream:
> * The <code>close</code> method of <code>FilterOutputStream</code>
> Perhaps something like the FilterOutputStream makes sense for us. But
> should we be ignoring exceptions on close? Perhaps we can close by
> trying to flush, then release the resource in a finally statement and
> STILL throw an exception to the caller.
FilterOutputStream is just a delegating OutputStream that allows the write() or
other methods to be overridden so that you can replace certain logic to create
filtered results. So, it has to call flush, close and everything else to meet
the contract of the I/O apis.
> We may need a different exception for flush than for the internal
> close, to facilitate debugging. Thus
> public void close() throws IOException, PortInUseException
The correct way to do this is to subclass an IOException to create
PortInUseException. Some applications might deal with it explicitly if they
allow multiple ports to be used, such as application using a bank of modems that
just needs one that's not in use. Others, might just let this exception go up
to a higher level to tell other code, or the user of the application that the
selected port is not available. At the application level, this exception can be
used to create small delays and retries while waiting for close to settle (if
close is done asynchronously).
Gregg Wonderly
More information about the Rxtx
mailing list