1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.nio.channels.spi; 19 20 import java.io.IOException; 21 import java.nio.channels.AsynchronousCloseException; 22 import java.nio.channels.Channel; 23 import java.nio.channels.ClosedByInterruptException; 24 import java.nio.channels.InterruptibleChannel; 25 26 /** 27 * {@code AbstractInterruptibleChannel} is the root class for interruptible 28 * channels. 29 * <p> 30 * The basic usage pattern for an interruptible channel is to invoke 31 * {@code begin()} before any I/O operation that potentially blocks 32 * indefinitely, then {@code end(boolean)} after completing the operation. The 33 * argument to the {@code end} method should indicate if the I/O operation has 34 * actually completed so that any change may be visible to the invoker. 35 */ 36 public abstract class AbstractInterruptibleChannel implements Channel, InterruptibleChannel { 37 38 private volatile boolean closed = false; 39 40 volatile boolean interrupted = false; 41 42 private final Runnable interruptAndCloseRunnable = new Runnable() { 43 @Override public void run() { 44 try { 45 interrupted = true; 46 AbstractInterruptibleChannel.this.close(); 47 } catch (IOException ignored) { 48 } 49 } 50 }; 51 AbstractInterruptibleChannel()52 protected AbstractInterruptibleChannel() { 53 } 54 isOpen()55 @Override public synchronized final boolean isOpen() { 56 return !closed; 57 } 58 59 /** 60 * Closes an open channel. If the channel is already closed then this method 61 * has no effect, otherwise it closes the receiver via the 62 * {@code implCloseChannel} method. 63 * <p> 64 * If an attempt is made to perform an operation on a closed channel then a 65 * {@link java.nio.channels.ClosedChannelException} is thrown. 66 * <p> 67 * If multiple threads attempt to simultaneously close a channel, then only 68 * one thread will run the closure code and the others will be blocked until 69 * the first one completes. 70 * 71 * @throws IOException 72 * if a problem occurs while closing this channel. 73 * @see java.nio.channels.Channel#close() 74 */ close()75 @Override public final void close() throws IOException { 76 if (!closed) { 77 synchronized (this) { 78 if (!closed) { 79 closed = true; 80 implCloseChannel(); 81 } 82 } 83 } 84 } 85 86 /** 87 * Indicates the beginning of a code section that includes an I/O operation 88 * that is potentially blocking. After this operation, the application 89 * should invoke the corresponding {@code end(boolean)} method. 90 */ begin()91 protected final void begin() { 92 Thread.currentThread().pushInterruptAction$(interruptAndCloseRunnable); 93 } 94 95 /** 96 * Indicates the end of a code section that has been started with 97 * {@code begin()} and that includes a potentially blocking I/O operation. 98 * 99 * @param success 100 * pass {@code true} if the blocking operation has succeeded and 101 * has had a noticeable effect; {@code false} otherwise. 102 * @throws AsynchronousCloseException 103 * if this channel is closed by another thread while this method 104 * is executing. 105 * @throws ClosedByInterruptException 106 * if another thread interrupts the calling thread while this 107 * method is executing. 108 */ end(boolean success)109 protected final void end(boolean success) throws AsynchronousCloseException { 110 Thread.currentThread().popInterruptAction$(interruptAndCloseRunnable); 111 if (interrupted) { 112 interrupted = false; 113 throw new ClosedByInterruptException(); 114 } 115 if (!success && closed) { 116 throw new AsynchronousCloseException(); 117 } 118 } 119 120 /** 121 * Implements the channel closing behavior. 122 * <p> 123 * Closes the channel with a guarantee that the channel is not currently 124 * closed through another invocation of {@code close()} and that the method 125 * is thread-safe. 126 * <p> 127 * Any outstanding threads blocked on I/O operations on this channel must be 128 * released with either a normal return code, or by throwing an 129 * {@code AsynchronousCloseException}. 130 * 131 * @throws IOException 132 * if a problem occurs while closing the channel. 133 */ implCloseChannel()134 protected abstract void implCloseChannel() throws IOException; 135 } 136