Gili
Gili

Reputation: 90200

Threading implications of AsynchronousByteChannel

The Javadoc for AsynchronousByteChannel.read() says the operation takes place asynchronously, but what happens when the end of stream has been reached? Is an implementation allowed to fire the completion handler within the same thread that invoked read()? From the implementation's point of view, there is no reason to carry out this operation asynchronously, because we already know the result. Similarly, if a user attempts to read into a ByteBuffer where remaining() returns 0 we know the read operation must return 0.

I ask because I've run into a race condition in my own AsynchronousByteChannel implementation. I'm invoking a completion handler that invokes notify() on itself when the operation completes. I then invoke the following user code:

CompletionHandler<?, ?> handler = ...;
synchronized (handler)
{
  asyncByteChannel.read(handler);
  handler.wait();
}

Note the user is assuming that the handler will be notified when the operation completes, but because read() actually invokes the completion-handler synchronously it gets notified before wait() and the latter will block forever.

Does the specification require me to update CompletionHandler in a separate thread or should users be aware of the fact that the thread that invokes read() may carry out some operations synchronously?

Upvotes: 3

Views: 1222

Answers (2)

Gili
Gili

Reputation: 90200

Looking at http://www.docjar.com/html/api/sun/nio/ch/AsynchronousSocketChannelImpl.java.html they always update CompletionHandler in a separate thread, but Future is updated in the same thread. Search for variable hasSpaceToRead to find the method in question.

I'm guessing their line of reasoning goes something like this:

  1. We create the Future returned to the user so there is no way he's interacting with it (synchronizing, etc.) before we return the object.
  2. The user creates the CompletionHandler, so we have no way of controlling what the implementation does (for all we know, we could trigger a deadlock!). Don't take a chance, fire the completion handler in a separate thread.

UPDATE: I stand corrected. According to Invoker.invoke() "If the current thread is in the channel group's thread pool then the handler is invoked directly, otherwise it is invoked indirectly."

SOLVED: According to http://download.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousChannelGroup.html "Where an I/O operation completes immediately, and the initiating thread is one of the pooled threads in the group then the completion handler may be invoked directly by the initiating thread."

Upvotes: 1

Paŭlo Ebermann
Paŭlo Ebermann

Reputation: 74800

Even when the handler is invoked on another thread, there is no guarantee it will be invoked after the read method returns, i.e. after your wait() started. (Okay, the synchronization lock seems to guarantee this.)

You should use synchronization and a boolean variable for the waiting and locking:

CompletionHandler<?, ?> handler = ...;
synchronized (handler)
{
   asyncByteChannel.read(handler);
   while(!handler.finished) {
     handler.wait();
   }
}

... and your handler will then set the finished variable to true.

Upvotes: 1

Related Questions