[Rxtx] flush twice, it is a long way to the kitchen
Joachim Buechse
joachim at buechse.de
Wed Sep 27 07:03:48 MDT 2006
I strongly opt to NOT call OutputStream.flush() in the implementation
of Port.close(). We COULD call it in OutputStream.close() even though
I would argue not to do it to motivate clean coding.
Flush has no timeout sematics. Calling OutputStream.flush() in
Port.close() effectively kills the only abort mechanism that exist.
I am aware, that many Java stream implementations call flush in the
close. Especially the higher level ones. This is all nice and handy
until you see the results in the real world. Missing flush calls and
flushes at the wrong moment are the source of 50% of performance
problems in network applications. I think it's a very bad idea
because relying on flush to get data delivered doesn't work anyway.
In most out of lab scenarios only one assumption holds: If the other
side (receiving application, not network stack) hasn't checked and
confirmed it, it didn't arrive.
In my opinion a SerialPort should react like a Socket. This is the
closest widely known equivalent existing in the Java standard
distribution. Calling close on a TCP-Socket (not the Outputstream of
the Socket!) closes this socket immediately without calling flush.
Best regards,
Joachim
---
Joachim Büchse
Softwarelösungen und Beratung
Hadlaubsteig 2
CH-8006 Zürich
On 27.09.2006, at 14:22, Dr. Douglas Lyon wrote:
> Hi All,
> Joachim suggests that:
> "This is the one point where I really disagree. Port.close() should
> never be used in the meaning of OutputStream.flush(). Port.close() is
> the only abort mechanism available. The native implementation should
> be free to throw away unsent data on close, abort reads and release
> the system resource as soon as possible."
>
> This may be a good point.
> http://java.sun.com/j2se/1.4.2/docs/api/java/io/
> OutputStream.html#close()
> Says that:
> Closes this output stream and releases any system
> resources associated with this stream. The
> general contract of close is that it closes the
> output stream. A closed stream cannot perform
> output operations and cannot be reopened.
>
> 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!
>
> 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()
> Shows no flush close. But the code at the top of the document shows:
> FileOutputStream fos = new FileOutputStream("t.tmp");
> ObjectOutputStream oos = new ObjectOutputStream(fos);
>
> oos.writeInt(12345);
> oos.writeObject("Today");
> oos.writeObject(new Date());
>
> oos.close();
>
> There is no flush in the example! Technically, according to the spec,
> this should write out NOTHING! But it DOES WORK!! Why?
>
> 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.
>
> 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.
>
> 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 {
> }
>
> Now check the FilterOutputStream:
> * The <code>close</code> method of <code>FilterOutputStream</code>
> * calls its <code>flush</code> method, and then calls the
> * <code>close</code> method of its underlying output stream.
> *
> * @exception IOException if an I/O error occurs.
> * @see java.io.FilterOutputStream#flush()
> * @see java.io.FilterOutputStream#out
> */
> public void close() throws IOException {
> try {
> flush();
> } catch (IOException ignored) {
> }
> out.close();
> }
>
> 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.
>
> We may need a different exception for flush than for the internal
> close, to facilitate debugging. Thus
> public void close() throws IOException, PortInUseException
>
> At present, we have:
> public interface CommPortInterface {
> /**
> * @return the name of this port.
> */
> String getName();
>
> /**
> * @return a string representation of this port.
> */
> String toString();
>
> /**
> * Closes this communications port. Further methods on this
> object will
> * throw IllegalStateException. All PortOwnershipListeners
> will be
> * notified of this change of ownership.
> */
> void close();
> }
>
>
> Now, RXTXPort shows:
> public synchronized void close() {
>
> Is this consistent with the present specification, and if not, what
> should
> we do?
>
> Thanks!
> - Doug
>
>
>> I always appreciate a discussion based on good arguments;-)
>>
>> I know that Linus T. has argued several times that non-blocking close
>> does not work. However in those cases where it does not work on the
>> kernel level it can not be made to work on the application level
>> neither. In my experience it is impossible to explain to a user why
>> an application "hangs" on close. However they do understand that it
>> may hang on open.
>>
>>>> From my experience a usefull implementation for close is a
>>>> timeout
>>>> based synchronous close with a rather small timeout. If the close
>>>> "works" (ie no kernel/driver problem preventing it's execution)
>>>> wait
>>>> until it has returned. If it blocks (ie more than 1..4 seconds)
>>>> return from the call. The trend for the kernel implementation of
>>>> close is clearly "abort" r/w operations. It used to be wait for
>>>> the
>>>> end of r/w operations.
>>>
>>> Well, serial data can flow slowly compared to what a program can
>>> generate. So,
>>> it should be possible for an application to open a port at 300baud,
>>> send some
>>> large amount of data and block on the close, waiting for the send
>>> to complete.
>>
>> This is the one point where I really disagree. Port.close() should
>> never be used in the meaning of OutputStream.flush(). Port.close() is
>> the only abort mechanism available. The native implementation should
>> be free to throw away unsend data on close, abort reads and release
>> the system resource as soon as possible.
>>
>>>> From a user perspective, closing a resource means "I lost all
>>>> interest in you". In the case where the (synchronous) close is
>>>> immediate that is no problem. However in the case where it is not,
>>>> this creates big problems. The application by itself has
>>>> basicly no
>>>> means of dealing with a blocking close. As the user has lost
>>>> interest
>>>> in the port he will not understand any kind of dialog regarding an
>>>> already closed port, he might have even decided to close the
>>>> application and see that it "hangs" on close.
>>>
>>> It is possible for a java application to do
>>>
>>> Thread th = new Thread() {
>>> public void run() {
>>> try {
>>> port.close();
>>> } catch( Exception ex ) {
>>> log.log( Level.WARNING, ex.toString(), ex );
>>> }
>>> }
>>> };
>>> th.setDaemon( true );
>>> th.run();
>>>
>>
>> 1st problem) This returns immediately (even in the case where close
>> does not block and returns within a few seconds). Hence the
>> application has no idea of the progress of the close, it has to join
>> the close thread or even have a callback interface to get status
>> updates. This is what I often do, but it is neither elegant nor
>> simple.
>> 2nd problem) The application has to keep track of ports which are in
>> the state of beeing closed if it wants to reuse/reopen the same ports
>> (race conditions).
>> 3rd problem) From what I have seen the java "process" will not
>> unwind/
>> return if a java thread hangs in a kernel call (daemon or not). That
>> problem most likely affects my prefered solution as well, but the
>> above code "suggests" otherwise.
>>
>>> ...implementation detail about networking. Serial ports don't have
>>> the same
>>> negociated close. Close progresses, unimpeded, when the write
>>> buffer is empty.
>>
>> In my experience the kernel level close may block if the USB driver
>> is trapped in a weird situation (ie a client device not reacting). It
>> may block even if no data remains to be send, I consider this a
>> kernel/driver/device bug - but unfortunately changes to the kernel/
>> driver/device are often impossible.
>>
>>> If remote flow control is asserted, there is no negociation for
>>> relief. This
>>> is why it seems interesting, to let the close happen
>>> asynchronously. In single
>>> threaded programming environments, it becomes very convenient to do
>>> this, but I
>>> don't think it's a correct behavior. When serial hardware/software
>>> is broken,
>>> and the flowcontrol never subsides, the application can hang
>>> forever.
>>
>> I have seen cases where unplugging a USB device at the "right moment"
>> or a buggy device that stops responding leads to a close blocking for
>> hours. This is nothing the application can influence. Arguing, that
>> an application should be allowed to hang if the OS/driver/hardware
>> has bugs works in theory but not in (my) practice. I have seen cases,
>> where (only) quitting the Java VM will unwind a hanging kernel close.
>> I can not explain this behaviour, but I can reproduce it with a buggy
>> USB device.
>>
>> Best regards,
>> Joachim
>>
>> _______________________________________________
>> Rxtx mailing list
>> Rxtx at qbang.org
>> http://mailman.qbang.org/mailman/listinfo/rxtx
>
>
> _______________________________________________
> Rxtx mailing list
> Rxtx at qbang.org
> http://mailman.qbang.org/mailman/listinfo/rxtx
More information about the Rxtx
mailing list