blob: 47f2512488bbac11cb019436b7f71f618dd0cfe6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.nio.channels.spi;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.Channel;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.InterruptibleChannel;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
/**
* {@code AbstractInterruptibleChannel} is the root class for interruptible
* channels.
* <p>
* The basic usage pattern for an interruptible channel is to invoke
* {@code begin()} before any I/O operation that potentially blocks
* indefinitely, then {@code end(boolean)} after completing the operation. The
* argument to the {@code end} method should indicate if the I/O operation has
* actually completed so that any change may be visible to the invoker.
*/
public abstract class AbstractInterruptibleChannel implements Channel,
InterruptibleChannel {
static Method setInterruptAction = null;
static {
try {
setInterruptAction = AccessController
.doPrivileged(new PrivilegedExceptionAction<Method>() {
public Method run() throws Exception {
return Thread.class.getDeclaredMethod(
"setInterruptAction",
new Class[] { Runnable.class });
}
});
setInterruptAction.setAccessible(true);
} catch (PrivilegedActionException e) {
// FIXME: be accommodate before VM actually provides
// setInterruptAction method
// throw new Error(e);
}
}
private volatile boolean closed = false;
volatile boolean interrupted = false;
/**
* Default constructor.
*/
protected AbstractInterruptibleChannel() {
super();
}
/**
* Indicates whether this channel is open.
*
* @return {@code true} if this channel is open, {@code false} if it is
* closed.
* @see java.nio.channels.Channel#isOpen()
*/
public synchronized final boolean isOpen() {
return !closed;
}
/**
* Closes an open channel. If the channel is already closed then this method
* has no effect, otherwise it closes the receiver via the
* {@code implCloseChannel} method.
* <p>
* If an attempt is made to perform an operation on a closed channel then a
* {@link java.nio.channels.ClosedChannelException} is thrown.
* <p>
* If multiple threads attempt to simultaneously close a channel, then only
* one thread will run the closure code and the others will be blocked until
* the first one completes.
*
* @throws IOException
* if a problem occurs while closing this channel.
* @see java.nio.channels.Channel#close()
*/
public final void close() throws IOException {
if (!closed) {
synchronized (this) {
if (!closed) {
closed = true;
implCloseChannel();
}
}
}
}
/**
* Indicates the beginning of a code section that includes an I/O operation
* that is potentially blocking. After this operation, the application
* should invoke the corresponding {@code end(boolean)} method.
*/
protected final void begin() {
// FIXME: be accommodate before VM actually provides
// setInterruptAction method
if (setInterruptAction != null) {
try {
setInterruptAction.invoke(Thread.currentThread(),
new Object[] { new Runnable() {
public void run() {
try {
interrupted = true;
AbstractInterruptibleChannel.this.close();
} catch (IOException e) {
// ignore
}
}
} });
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* Indicates the end of a code section that has been started with
* {@code begin()} and that includes a potentially blocking I/O operation.
*
* @param success
* pass {@code true} if the blocking operation has succeeded and
* has had a noticeable effect; {@code false} otherwise.
* @throws AsynchronousCloseException
* if this channel is closed by another thread while this method
* is executing.
* @throws ClosedByInterruptException
* if another thread interrupts the calling thread while this
* method is executing.
*/
protected final void end(boolean success) throws AsynchronousCloseException {
// FIXME: be accommodate before VM actually provides
// setInterruptAction method
if (setInterruptAction != null) {
try {
setInterruptAction.invoke(Thread.currentThread(),
new Object[] { null });
} catch (Exception e) {
throw new RuntimeException(e);
}
if (interrupted) {
interrupted = false;
throw new ClosedByInterruptException();
}
}
if (!success && closed) {
throw new AsynchronousCloseException();
}
}
/**
* Implements the channel closing behavior.
* <p>
* Closes the channel with a guarantee that the channel is not currently
* closed through another invocation of {@code close()} and that the method
* is thread-safe.
* <p>
* Any outstanding threads blocked on I/O operations on this channel must be
* released with either a normal return code, or by throwing an
* {@code AsynchronousCloseException}.
*
* @throws IOException
* if a problem occurs while closing the channel.
*/
protected abstract void implCloseChannel() throws IOException;
}