| /* |
| * 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; |
| } |