[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