[Rxtx] close != flush and may != close!
Gregg Wonderly
gergg at cox.net
Thu Sep 28 19:14:00 MDT 2006
Joachim Buechse wrote:
> Port.IS.read() and Port.OS.write() can be implemented to always
> unwind at Port.close() (even though this may require the use of non-
> blocking IO or even a separate thread depending on the features of
> the OS).
What I worry the most about is that your implementation on different OSes will
behave so dramatically different that an application written to use the API will
not work reliably. I.e. if different techniques at the source level are
necessary to deal with OS and driver issues, because you chose to take advantage
of some features on one OS that are not available on another.
This is why I'd really like for the stuff that you are describing to not be the
implementation, but to be a layer on top of the implementation that you or
someone else can write/use to solve the problems that you have with the standard
API.
Look at the abstractions that Swing uses to mask the features and implementation
details of multiple different graphics implementations. Only by not manifesting
the OS/graphics behaviors into the APIs is it possible to write swing code that
is portable.
> Even if read+write don't unwind, I disagree that the user hasn't
> gained anything from a non-blocking close. A blocking Read or Write
> might have finished before the execution of Abort, or as a
> successfull result of Abort (read blocked by write on Palm OS), or as
> a non successfull result of Abort. I still have to see an application
> that reliably uses results obtained from a Context that was aborted.
I think we are talking past each other on this issue. You are talking about
lots of different OS and driver issues. I'm trying to suggest that while those
are interesting and valid concerns, direct treatment of them doesn't belong as a
visible part, or behavior of the API and its operational characteristics.
> In GUI apps 99% of all cases Abort is the users way of saying: "Hey
> something went wrong that the application can't fix, I'll try a
> different port/server/etc."
Again, I think that Port.close not flushing is an okay concept, but I worry that
concurrency will make that difficult to get exactly write in all cases. If
there is the tiniest chance that it won't work, then an application can only
provide access to that as one of the solutions. This makes the application much
more complex.
> In server environments non-blocking abort can be used by a supervisor
> thread that controls the execution of worker threads. In production
> environments I have seen several cases where Oracle connections, TCP
> Sockets, Serial Ports, or any other "device" will simply block
> ignoring all attempts to shut it down. An application will always
> have to live with that, so a (rare;-) unwinding read/write must be
> acceptable. However a blocking close() will often have catastrophic
> results as people tend to call close() in finalize().
Again, OutputStream/InputStream.close() are going to be called, and shut rightly
flush(). This means that anytime Port.close() is called, you have a 50% chance
of needing to deal with unwinding a read/write already in the OS trying to shut
things down. It is that contention and how it is handled that makes things
really more difficult. So, if some thread or operational thread of execution is
in the OS, and this keeps your port.close() from working, that will be a problem
anyway. I guess I just don't see how adding all the complexity really solves
the problem. I merely defers it, or moves the discovery or encounter with the
issue to another place in the execution path.
> What I usually do in my GUI applications is the creation of async-
> workers:
>
> - A worker gets all data that needs to be send.
> - It returns the received data as an Object or byte[] (potentially
> via EventQueue.invokeLater())
> - It can report progress via a small API (potentially via
> EventQueue.invokeLater())
> - It has no access to application data structures.
> - It has an abort method.
>
> The abort method is non-blocking and makes sure that neither status
> nor result are returned "to the main application" after the abort
> function returned.
>
> This has been proven to be a very robust concept for UI driven
> applications. It confines synchronisation to a very small part of the
> application, it's reasonably simple to understand and implement.
> Unless your data communication is highly "interactive" with the
> application data structures it works like a charm and simplifies the
> application design considerably.
Using EventQueue is a convienent way to serialize operations so that you don't
have to worry so much about resource contention. The java.util.concurrent stuff
makes that kind of stuff accessible to the masses who might not be as familar
with the issues of concurrent programming in Java.
I appreciate that you find value in the ability to abort I/O operations. I use
Java's async Socket.close() behavior all the time to halt separate threads that
are reading/writing on a socket. It's very convienent.
Gregg Wonderly
More information about the Rxtx
mailing list