AsynchrronousByteChannel vs AsynchronousFileChannel andAsynchronousTransmitter

Alexander Libman libman at terabit.com.au
Mon Apr 27 22:32:45 PDT 2009


Hi Alan,

> >
> AsynchronousFileChannel is intended for concurrent access and so does
> not maintain a global file position (and so intentionally does not
> implement AsynchrnonousByteChannel). AsynchronousFileChannel isn't
> really intended for sequential access but it would be relatively simple
> to wrap an AsynchronousFileChannel, returning a
> AsynchrnonousByteChannel.

Agree. Basically the question was what was the better solution:
add to the AsynchronousFileChannel some extra features or
to develop a small wrapper like AsynchronousByteFileChannel (may be you can
advise the better name).
Seems, the time spent on writing AsynchronousByteFileChannel was not wasted
:)

> The resulting AsynchrnonousByteChannel would
> maintain the file position. It probably needs experimentation to see if
> one position for reading and another for writing, or simple one global
> position if better (having independent positions for read and write
> might be a bit strange but easier than trying to coordinate concurrent
> read/write operations).

It is not a problem to support single position for the
AsynchrnousByteFileChannel.
The choice of two independent read and write positions was made
deliberately.
It allows to think about file as two independent streams - one for reading,
another for writing.
It also very convinient for the model
read-updateInPlace-writeOnSamePosition.
Another reason is : since a position can be updated only on inner
AsychrnousFileChannel's operation completion,
two positions will make possible to support one outstanding read and one
outstanding write simultaneously.
If you think that one position is better solution, then  it is also good:
the developer can create two "AsynchrnousByteFileChannels" and
use one for reading and second for writing. What is the preferred solution,
I do not know yet too.
Yes, need to experiment more.

Now, coming back to the AsynchrnousTransmitter:
> >
> > AsynchronousTransmitter {
> >
> > private AsynchrnousTransmitter (....);
> >
> > public static <A>
> >      Future<AsynchronousTransmitter>
> >           transmit(AsynchronousChannel inChannel,
> >                    AsynchronousChannel outChannel,
> > 		       A attachment,
> > 	             CompletionHandler<AsynchronousTransmitter, ?
> super A> handler)
> >
> > /* The AsynchrnousTransmitter instance is accessible via
> returned Future<>
> > */
> > public long getTotalBytesRead ();
> > public long getTotalBytesWritten ();
> > }
> >
> > where inChannel and outChannel can be any combination of
> > AsynchrnousByteChannel/AsynchronousFileChannel(s).
> >
>
> I'm trying to understand your class from the above skeleton. Who
> executes the completion handler? Is the Transmitter created with its own
> thread pool or is the a thread associated with the source or target
> channel's thread pool?

For this pure Java solution we do not have to specify thread pool.
After initiation of the first read, Transmitter initiates next operations
from the completion handler for the previous operation.
So it works in both thread pools: for source and destination.
But it does not take any significant time as
what we have to do is to start next operation(s) only.
It uses two buffers and zero-copy technique.
It does not use any locks and buffer allocations.
It works only  with couple AtomicReferences for
keeping next buffer handles.

Basicically it works as follows:
1) start Read on InputChannel into buffer1;
   return;

2) readCompleted for buffer1:

   if (endOfData and all writes are done)
   {
       invokeUserCompletionHandler
       return
   }

   if (there is no pending write on OutputChannel)
   {
   	start Write on OutputChannel from buffer1;
   }
   else
   {
       store buffer1 as next buffer to write
   }

   if (buffer2 is free)
   {
	start Read on InputChannel into buffer2;
   }

3) writeCompleted on buffer2
   if (endOfData and all is done)
   {
       invokeUserCompletionHandler
       return
   }

   if (there is no pending read on InputChannel)
   {
	start Read on InputChannel into buffer2;
   }
   else
   {
      store buffer2 as next buffer to read
   }

   if (buffer1 contains data to write)
   {
  	start Write on OutputChannel from buffer1;
   }

I skipped synchronization details (buffer1 and buffer2 are AtomicReferences
and used simmetrically),
but you see that completion handler does not do much.

We need our own thread pool in two cases when we deal with filters and
artificial asynchornous objects like Transmitter:
a) when a completion handler's execution takes a long time ( SSLEngine has a
task to run, etc)
or
b) when an operation started on outer channel has immediate completion
without any actions performed on inner channel.


>
> From an initial glance it looks like you might need to result's
>completed rather than failed method for the case that there is an I/O
>failure after some bytes have been transferred.

yes, we keep this in mind.
Currently, there is one more method-accessor in AsynchronousTransmitter :
boolean isSuccess ();

>
> This FileChannel transfer methods also work this way so that the best
> mechanism is used. We don't have transfer methods in the asynchronous
> I/O API yet but I would like to add some support one day. A possible
> candidate is AsynchronousSocketChannel#transferFrom(FileChannel,
> position, count) that works like the write method. This can use the best
> underlying mechanism for the case that the respective providers are the
> default providers.

This would be nice. Here we may need to specify which thread pool to use:
source or destination.
For Windows such "transferFrom" obviously can be mapped to
TransmitFile/TransmitPackets
and all is clear - no need to specify thread pool.
But for POSIX different flavors (Linux, Sun, BSD)  sendfile() may block and
we need to specify thread pool executor (source, destination or special
executor).

If we look at Windows evolution, we can find that initially it was
introduced only TransmitFile API call and
later Microsoft added TransmitPackets. So my point is that "transferFrom" is
necessary, but may be not enough.
The class kind of AsynchrnousTransmitter can be used and extended to execute
some sofisticated sequences and chains of operations on basic
AsynchronousChannels and filters.

Alex








More information about the nio-dev mailing list