/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* @(#)AbstractInterruptibleChannel.java 1.15 03/12/19
*/
package java.nio.channels.spi;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.nio.ch.Interruptible;
/**
* Base implementation class for interruptible channels.
*
* <p> This class encapsulates the low-level machinery required to implement
* the asynchronous closing and interruption of channels. A concrete channel
* class must invoke the {@link #begin begin} and {@link #end end} methods
* before and after, respectively, invoking an I/O operation that might block
* indefinitely. In order to ensure that the {@link #end end} method is always
* invoked, these methods should be used within a
* <tt>try</tt> ... <tt>finally</tt> block: <a name="be">
*
* <blockquote><pre>
* boolean completed = false;
* try {
* begin();
* completed = ...; // Perform blocking I/O operation
* return ...; // Return result
* } finally {
* end(completed);
* }</pre></blockquote>
*
* <p> The <tt>completed</tt> argument to the {@link #end end} method tells
* whether or not the I/O operation actually completed, that is, whether it had
* any effect that would be visible to the invoker. In the case of an
* operation that reads bytes, for example, this argument should be
* <tt>true</tt> if, and only if, some bytes were actually transferred into the
* invoker's target buffer.
*
* <p> A concrete channel class must also implement the {@link
* #implCloseChannel implCloseChannel} method in such a way that if it is
* invoked while another thread is blocked in a native I/O operation upon the
* channel then that operation will immediately return, either by throwing an
* exception or by returning normally. If a thread is interrupted or the
* channel upon which it is blocked is asynchronously closed then the channel's
* {@link #end end} method will throw the appropriate exception.
*
* <p> This class performs the synchronization required to implement the {@link
* java.nio.channels.Channel} specification. Implementations of the {@link
* #implCloseChannel implCloseChannel} method need not synchronize against
* other threads that might be attempting to close the channel. </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @version 1.15, 03/12/19
* @since 1.4
*/
public abstract class AbstractInterruptibleChannel
implements Channel, InterruptibleChannel
{
private Object closeLock = new Object();
private volatile boolean open = true;
/**
* Initializes a new instance of this class.
*/
protected AbstractInterruptibleChannel() { }
/**
* Closes this channel.
*
* <p> If the channel has already been closed then this method returns
* immediately. Otherwise it marks the channel as closed and then invokes
* the {@link #implCloseChannel implCloseChannel} method in order to
* complete the close operation. </p>
*
* @throws IOException
* If an I/O error occurs
*/
public final void close() throws IOException {
synchronized (closeLock) {
if (!open)
return;
open = false;
implCloseChannel();
}
}
/**
* Closes this channel.
*
* <p> This method is invoked by the {@link #close close} method in order
* to perform the actual work of closing the channel. This method is only
* invoked if the channel has not yet been closed, and it is never invoked
* more than once.
*
* <p> An implementation of this method must arrange for any other thread
* that is blocked in an I/O operation upon this channel to return
* immediately, either by throwing an exception or by returning normally.
* </p>
*
* @throws IOException
* If an I/O error occurs while closing the channel
*/
protected abstract void implCloseChannel() throws IOException;
public final boolean isOpen() {
return open;
}
// -- Interruption machinery --
private Interruptible interruptor;
private volatile boolean interrupted = false;
/**
* Marks the beginning of an I/O operation that might block indefinitely.
*
* <p> This method should be invoked in tandem with the {@link #end end}
* method, using a <tt>try</tt> ... <tt>finally</tt> block as
* shown <a href="#be">above</a>, in order to implement asynchronous
* closing and interruption for this channel. </p>
*/
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt() {
synchronized (closeLock) {
if (!open)
return;
interrupted = true;
open = false;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
if (Thread.currentThread().isInterrupted())
interruptor.interrupt();
}
/**
* Marks the end of an I/O operation that might block indefinitely.
*
* <p> This method should be invoked in tandem with the {@link #begin
* begin} method, using a <tt>try</tt> ... <tt>finally</tt> block
* as shown <a href="#be">above</a>, in order to implement asynchronous
* closing and interruption for this channel. </p>
*
* @param completed
* <tt>true</tt> if, and only if, the I/O operation completed
* successfully, that is, had some effect that would be visible to
* the operation's invoker
*
* @throws AsynchronousCloseException
* If the channel was asynchronously closed
*
* @throws ClosedByInterruptException
* If the thread blocked in the I/O operation was interrupted
*/
protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);
if (completed) {
interrupted = false;
return;
}
if (interrupted) throw new ClosedByInterruptException();
if (!open) throw new AsynchronousCloseException();
}
// -- Reflection hackery --
private static Method blockedOnMethod = null;
static void blockedOn(Interruptible intr) { // package-private
if (blockedOnMethod == null)
initBlockedOn();
try {
blockedOnMethod.invoke(Thread.currentThread(),
new Object[] { intr });
} catch (IllegalAccessException x) {
throw new Error(x);
} catch (IllegalArgumentException x) {
throw new Error(x);
} catch (InvocationTargetException x) {
throw new Error(x);
}
}
private static void initBlockedOn() {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
Class th = Class.forName("java.lang.Thread");
blockedOnMethod
= th.getDeclaredMethod("blockedOn",
new Class[] { Interruptible.class });
blockedOnMethod.setAccessible(true);
} catch (ClassNotFoundException x) {
throw new Error(x);
} catch (NoSuchMethodException x) {
throw new Error(x);
} catch (IllegalArgumentException x) {
throw new Error(x);
} catch (ClassCastException x) {
throw new Error(x);
}
return null;
}});
}
// Workaround for apparent VM bug: Sometimes an interrupted thread
// cannot load a class
private static class FooChannel extends AbstractInterruptibleChannel {
protected void implCloseChannel() { }
}
static {
FooChannel fc = new FooChannel();
fc.begin();
try {
fc.end(true);
} catch (IOException e) { }
}
}
|