1 /*
2 * Copyright (c) 2000, 2010, 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 final 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 Thread interrupted;
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(Thread target) {
159 synchronized (closeLock) {
160 if (!open)
161 return;
162 open = false;
163 interrupted = target;
164 try {
165 AbstractInterruptibleChannel.this.implCloseChannel();
166 } catch (IOException x) { }
167 }
168 }};
169 }
170 blockedOn(interruptor);
171 Thread me = Thread.currentThread();
172 if (me.isInterrupted())
173 interruptor.interrupt(me);
174 }
175
176 /**
177 * Marks the end of an I/O operation that might block indefinitely.
178 *
179 * <p> This method should be invoked in tandem with the {@link #begin
180 * begin} method, using a <tt>try</tt> ... <tt>finally</tt> block
181 * as shown <a href="#be">above</a>, in order to implement asynchronous
182 * closing and interruption for this channel. </p>
183 *
184 * @param completed
185 * <tt>true</tt> if, and only if, the I/O operation completed
186 * successfully, that is, had some effect that would be visible to
187 * the operation's invoker
188 *
189 * @throws AsynchronousCloseException
190 * If the channel was asynchronously closed
191 *
192 * @throws ClosedByInterruptException
193 * If the thread blocked in the I/O operation was interrupted
194 */
195 protected final void end(boolean completed)
196 throws AsynchronousCloseException
197 {
198 blockedOn(null);
199 Thread interrupted = this.interrupted;
200 if (interrupted != null && interrupted == Thread.currentThread()) {
201 interrupted = null;
202 throw new ClosedByInterruptException();
203 }
204 if (!completed && !open)
205 throw new AsynchronousCloseException();
206 }
207
208
209 // -- sun.misc.SharedSecrets --
210 static void blockedOn(Interruptible intr) { // package-private
211 sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),
212 intr);
213 }
214 }