1 /* 2 * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 */ 28 29 package java.nio.channels.spi; 30 31 import java.io.IOException; 32 import java.lang.reflect.Method; 33 import java.lang.reflect.InvocationTargetException; 34 import java.nio.channels.*; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import sun.nio.ch.Interruptible; 38 39 40 /** 41 * Base implementation class for interruptible channels. 42 * 43 * <p> This class encapsulates the low-level machinery required to implement 44 * the asynchronous closing and interruption of channels. A concrete channel 45 * class must invoke the {@link #begin begin} and {@link #end end} methods 46 * before and after, respectively, invoking an I/O operation that might block 47 * indefinitely. In order to ensure that the {@link #end end} method is always 48 * invoked, these methods should be used within a 49 * <tt>try</tt> ... <tt>finally</tt> block: <a name="be"> 50 * 51 * <blockquote><pre> 52 * boolean completed = false; 53 * try { 54 * begin(); 55 * completed = ...; // Perform blocking I/O operation 56 * return ...; // Return result 57 * } finally { 58 * end(completed); 59 * }</pre></blockquote> 60 * 61 * <p> The <tt>completed</tt> argument to the {@link #end end} method tells 62 * whether or not the I/O operation actually completed, that is, whether it had 63 * any effect that would be visible to the invoker. In the case of an 64 * operation that reads bytes, for example, this argument should be 65 * <tt>true</tt> if, and only if, some bytes were actually transferred into the 66 * invoker's target buffer. 67 * 68 * <p> A concrete channel class must also implement the {@link 69 * #implCloseChannel implCloseChannel} method in such a way that if it is 70 * invoked while another thread is blocked in a native I/O operation upon the 71 * channel then that operation will immediately return, either by throwing an 72 * exception or by returning normally. If a thread is interrupted or the 73 * channel upon which it is blocked is asynchronously closed then the channel's 74 * {@link #end end} method will throw the appropriate exception. 75 * 76 * <p> This class performs the synchronization required to implement the {@link 77 * java.nio.channels.Channel} specification. Implementations of the {@link 78 * #implCloseChannel implCloseChannel} method need not synchronize against 79 * other threads that might be attempting to close the channel. </p> 80 * 81 * 82 * @author Mark Reinhold 83 * @author JSR-51 Expert Group 84 * @since 1.4 85 */ 86 87 public abstract class AbstractInterruptibleChannel 88 implements Channel, InterruptibleChannel 89 { 90 91 private Object closeLock = new Object(); 92 private volatile boolean open = true; 93 94 /** 95 * Initializes a new instance of this class. 96 */ 97 protected AbstractInterruptibleChannel() { } 98 99 /** 100 * Closes this channel. 101 * 102 * <p> If the channel has already been closed then this method returns 103 * immediately. Otherwise it marks the channel as closed and then invokes 104 * the {@link #implCloseChannel implCloseChannel} method in order to 105 * complete the close operation. </p> 106 * 107 * @throws IOException 108 * If an I/O error occurs 109 */ 110 public final void close() throws IOException { 111 synchronized (closeLock) { 112 if (!open) 113 return; 114 open = false; 115 implCloseChannel(); 116 } 117 } 118 119 /** 120 * Closes this channel. 121 * 122 * <p> This method is invoked by the {@link #close close} method in order 123 * to perform the actual work of closing the channel. This method is only 124 * invoked if the channel has not yet been closed, and it is never invoked 125 * more than once. 126 * 127 * <p> An implementation of this method must arrange for any other thread 128 * that is blocked in an I/O operation upon this channel to return 129 * immediately, either by throwing an exception or by returning normally. 130 * </p> 131 * 132 * @throws IOException 133 * If an I/O error occurs while closing the channel 134 */ 135 protected abstract void implCloseChannel() throws IOException; 136 137 public final boolean isOpen() { 138 return open; 139 } 140 141 142 // -- Interruption machinery -- 143 144 private Interruptible interruptor; 145 private volatile boolean interrupted = false; 146 147 /** 148 * Marks the beginning of an I/O operation that might block indefinitely. 149 * 150 * <p> This method should be invoked in tandem with the {@link #end end} 151 * method, using a <tt>try</tt> ... <tt>finally</tt> block as 152 * shown <a href="#be">above</a>, in order to implement asynchronous 153 * closing and interruption for this channel. </p> 154 */ 155 protected final void begin() { 156 if (interruptor == null) { 157 interruptor = new Interruptible() { 158 public void interrupt() { 159 synchronized (closeLock) { 160 if (!open) 161 return; 162 interrupted = true; 163 open = false; 164 try { 165 AbstractInterruptibleChannel.this.implCloseChannel(); 166 } catch (IOException x) { } 167 } 168 }}; 169 } 170 blockedOn(interruptor); 171 if (Thread.currentThread().isInterrupted()) 172 interruptor.interrupt(); 173 } 174 175 /** 176 * Marks the end of an I/O operation that might block indefinitely. 177 * 178 * <p> This method should be invoked in tandem with the {@link #begin 179 * begin} method, using a <tt>try</tt> ... <tt>finally</tt> block 180 * as shown <a href="#be">above</a>, in order to implement asynchronous 181 * closing and interruption for this channel. </p> 182 * 183 * @param completed 184 * <tt>true</tt> if, and only if, the I/O operation completed 185 * successfully, that is, had some effect that would be visible to 186 * the operation's invoker 187 * 188 * @throws AsynchronousCloseException 189 * If the channel was asynchronously closed 190 * 191 * @throws ClosedByInterruptException 192 * If the thread blocked in the I/O operation was interrupted 193 */ 194 protected final void end(boolean completed) 195 throws AsynchronousCloseException 196 { 197 blockedOn(null); 198 if (completed) { 199 interrupted = false; 200 return; 201 } 202 if (interrupted) throw new ClosedByInterruptException(); 203 if (!open) throw new AsynchronousCloseException(); 204 } 205 206 207 // -- sun.misc.SharedSecrets -- 208 static void blockedOn(Interruptible intr) { // package-private 209 sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), 210 intr); 211 } 212 }