001 /*
002 * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 /*
027 * @(#)AbstractInterruptibleChannel.java 1.23 07/05/05
028 */
029
030 package java.nio.channels.spi;
031
032 import java.io.IOException;
033 import java.lang.reflect.Method;
034 import java.lang.reflect.InvocationTargetException;
035 import java.nio.channels.*;
036 import java.security.AccessController;
037 import java.security.PrivilegedAction;
038 import sun.nio.ch.Interruptible;
039
040 /**
041 * Base implementation class for interruptible channels.
042 *
043 * <p> This class encapsulates the low-level machinery required to implement
044 * the asynchronous closing and interruption of channels. A concrete channel
045 * class must invoke the {@link #begin begin} and {@link #end end} methods
046 * before and after, respectively, invoking an I/O operation that might block
047 * indefinitely. In order to ensure that the {@link #end end} method is always
048 * invoked, these methods should be used within a
049 * <tt>try</tt> ... <tt>finally</tt> block: <a name="be">
050 *
051 * <blockquote><pre>
052 * boolean completed = false;
053 * try {
054 * begin();
055 * completed = ...; // Perform blocking I/O operation
056 * return ...; // Return result
057 * } finally {
058 * end(completed);
059 * }</pre></blockquote>
060 *
061 * <p> The <tt>completed</tt> argument to the {@link #end end} method tells
062 * whether or not the I/O operation actually completed, that is, whether it had
063 * any effect that would be visible to the invoker. In the case of an
064 * operation that reads bytes, for example, this argument should be
065 * <tt>true</tt> if, and only if, some bytes were actually transferred into the
066 * invoker's target buffer.
067 *
068 * <p> A concrete channel class must also implement the {@link
069 * #implCloseChannel implCloseChannel} method in such a way that if it is
070 * invoked while another thread is blocked in a native I/O operation upon the
071 * channel then that operation will immediately return, either by throwing an
072 * exception or by returning normally. If a thread is interrupted or the
073 * channel upon which it is blocked is asynchronously closed then the channel's
074 * {@link #end end} method will throw the appropriate exception.
075 *
076 * <p> This class performs the synchronization required to implement the {@link
077 * java.nio.channels.Channel} specification. Implementations of the {@link
078 * #implCloseChannel implCloseChannel} method need not synchronize against
079 * other threads that might be attempting to close the channel. </p>
080 *
081 *
082 * @author Mark Reinhold
083 * @author JSR-51 Expert Group
084 * @version 1.23, 07/05/05
085 * @since 1.4
086 */
087
088 public abstract class AbstractInterruptibleChannel implements Channel,
089 InterruptibleChannel {
090
091 private Object closeLock = new Object();
092 private volatile boolean open = true;
093
094 /**
095 * Initializes a new instance of this class.
096 */
097 protected AbstractInterruptibleChannel() {
098 }
099
100 /**
101 * Closes this channel.
102 *
103 * <p> If the channel has already been closed then this method returns
104 * immediately. Otherwise it marks the channel as closed and then invokes
105 * the {@link #implCloseChannel implCloseChannel} method in order to
106 * complete the close operation. </p>
107 *
108 * @throws IOException
109 * If an I/O error occurs
110 */
111 public final void close() throws IOException {
112 synchronized (closeLock) {
113 if (!open)
114 return;
115 open = false;
116 implCloseChannel();
117 }
118 }
119
120 /**
121 * Closes this channel.
122 *
123 * <p> This method is invoked by the {@link #close close} method in order
124 * to perform the actual work of closing the channel. This method is only
125 * invoked if the channel has not yet been closed, and it is never invoked
126 * more than once.
127 *
128 * <p> An implementation of this method must arrange for any other thread
129 * that is blocked in an I/O operation upon this channel to return
130 * immediately, either by throwing an exception or by returning normally.
131 * </p>
132 *
133 * @throws IOException
134 * If an I/O error occurs while closing the channel
135 */
136 protected abstract void implCloseChannel() throws IOException;
137
138 public final boolean isOpen() {
139 return open;
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
166 .implCloseChannel();
167 } catch (IOException x) {
168 }
169 }
170 }
171 };
172 }
173 blockedOn(interruptor);
174 if (Thread.currentThread().isInterrupted())
175 interruptor.interrupt();
176 }
177
178 /**
179 * Marks the end of an I/O operation that might block indefinitely.
180 *
181 * <p> This method should be invoked in tandem with the {@link #begin
182 * begin} method, using a <tt>try</tt> ... <tt>finally</tt> block
183 * as shown <a href="#be">above</a>, in order to implement asynchronous
184 * closing and interruption for this channel. </p>
185 *
186 * @param completed
187 * <tt>true</tt> if, and only if, the I/O operation completed
188 * successfully, that is, had some effect that would be visible to
189 * the operation's invoker
190 *
191 * @throws AsynchronousCloseException
192 * If the channel was asynchronously closed
193 *
194 * @throws ClosedByInterruptException
195 * If the thread blocked in the I/O operation was interrupted
196 */
197 protected final void end(boolean completed)
198 throws AsynchronousCloseException {
199 blockedOn(null);
200 if (completed) {
201 interrupted = false;
202 return;
203 }
204 if (interrupted)
205 throw new ClosedByInterruptException();
206 if (!open)
207 throw new AsynchronousCloseException();
208 }
209
210 // -- sun.misc.SharedSecrets --
211 static void blockedOn(Interruptible intr) { // package-private
212 sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(
213 Thread.currentThread(), intr);
214 }
215 }
|