/*
* 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.nio.channels.AsynchronousCloseException;
import java.nio.channels.Channel;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.InterruptibleChannel;
/**
* {@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 {
private volatile boolean closed = false;
volatile boolean interrupted = false;
private final Runnable interruptAndCloseRunnable = new Runnable() {
@Override public void run() {
try {
interrupted = true;
AbstractInterruptibleChannel.this.close();
} catch (IOException ignored) {
}
}
};
protected AbstractInterruptibleChannel() {
}
@Override 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()
*/
@Override 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() {
Thread.currentThread().pushInterruptAction$(interruptAndCloseRunnable);
}
/**
* 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 {
Thread.currentThread().popInterruptAction$(interruptAndCloseRunnable);
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;
}