1 /*
2 * Copyright 2000-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 }