1 /*
2 * Copyright (c) 2000, 2018, 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.nio.channels.*;
33 import jdk.internal.misc.SharedSecrets;
34 import sun.nio.ch.Interruptible;
35
36
37 /**
38 * Base implementation class for interruptible channels.
39 *
40 * <p> This class encapsulates the low-level machinery required to implement
41 * the asynchronous closing and interruption of channels. A concrete channel
42 * class must invoke the {@link #begin begin} and {@link #end end} methods
43 * before and after, respectively, invoking an I/O operation that might block
44 * indefinitely. In order to ensure that the {@link #end end} method is always
45 * invoked, these methods should be used within a
46 * {@code try} ... {@code finally} block:
47 *
48 * <blockquote><pre id="be">
49 * boolean completed = false;
50 * try {
51 * begin();
52 * completed = ...; // Perform blocking I/O operation
53 * return ...; // Return result
54 * } finally {
55 * end(completed);
56 * }</pre></blockquote>
57 *
58 * <p> The {@code completed} argument to the {@link #end end} method tells
59 * whether or not the I/O operation actually completed, that is, whether it had
60 * any effect that would be visible to the invoker. In the case of an
61 * operation that reads bytes, for example, this argument should be
62 * {@code true} if, and only if, some bytes were actually transferred into the
63 * invoker's target buffer.
64 *
65 * <p> A concrete channel class must also implement the {@link
66 * #implCloseChannel implCloseChannel} method in such a way that if it is
67 * invoked while another thread is blocked in a native I/O operation upon the
68 * channel then that operation will immediately return, either by throwing an
69 * exception or by returning normally. If a thread is interrupted or the
70 * channel upon which it is blocked is asynchronously closed then the channel's
71 * {@link #end end} method will throw the appropriate exception.
72 *
73 * <p> This class performs the synchronization required to implement the {@link
74 * java.nio.channels.Channel} specification. Implementations of the {@link
75 * #implCloseChannel implCloseChannel} method need not synchronize against
76 * other threads that might be attempting to close the channel. </p>
77 *
78 *
79 * @author Mark Reinhold
80 * @author JSR-51 Expert Group
81 * @since 1.4
82 */
83
84 public abstract class AbstractInterruptibleChannel
85 implements Channel, InterruptibleChannel
86 {
87
88 private final Object closeLock = new Object();
89 private volatile boolean closed;
90
91 /**
92 * Initializes a new instance of this class.
93 */
94 protected AbstractInterruptibleChannel() { }
95
96 /**
97 * Closes this channel.
98 *
99 * <p> If the channel has already been closed then this method returns
100 * immediately. Otherwise it marks the channel as closed and then invokes
101 * the {@link #implCloseChannel implCloseChannel} method in order to
102 * complete the close operation. </p>
103 *
104 * @throws IOException
105 * If an I/O error occurs
106 */
107 public final void close() throws IOException {
108 synchronized (closeLock) {
109 if (closed)
110 return;
111 closed = true;
112 implCloseChannel();
113 }
114 }
115
116 /**
117 * Closes this channel.
118 *
119 * <p> This method is invoked by the {@link #close close} method in order
120 * to perform the actual work of closing the channel. This method is only
121 * invoked if the channel has not yet been closed, and it is never invoked
122 * more than once.
123 *
124 * <p> An implementation of this method must arrange for any other thread
125 * that is blocked in an I/O operation upon this channel to return
126 * immediately, either by throwing an exception or by returning normally.
127 * </p>
128 *
129 * @throws IOException
130 * If an I/O error occurs while closing the channel
131 */
132 protected abstract void implCloseChannel() throws IOException;
133
134 public final boolean isOpen() {
135 return !closed;
136 }
137
138
139 // -- Interruption machinery --
140
141 private Interruptible interruptor;
142 private volatile Thread interrupted;
143
144 /**
145 * Marks the beginning of an I/O operation that might block indefinitely.
146 *
147 * <p> This method should be invoked in tandem with the {@link #end end}
148 * method, using a {@code try} ... {@code finally} block as
149 * shown <a href="#be">above</a>, in order to implement asynchronous
150 * closing and interruption for this channel. </p>
151 */
152 protected final void begin() {
153 if (interruptor == null) {
154 interruptor = new Interruptible() {
155 public void interrupt(Thread target) {
156 synchronized (closeLock) {
157 if (closed)
158 return;
159 closed = true;
160 interrupted = target;
161 try {
162 AbstractInterruptibleChannel.this.implCloseChannel();
163 } catch (IOException x) { }
164 }
165 }};
166 }
167 blockedOn(interruptor);
168 Thread me = Thread.currentThread();
169 if (me.isInterrupted())
170 interruptor.interrupt(me);
171 }
172
173 /**
174 * Marks the end of an I/O operation that might block indefinitely.
175 *
176 * <p> This method should be invoked in tandem with the {@link #begin
177 * begin} method, using a {@code try} ... {@code finally} block
178 * as shown <a href="#be">above</a>, in order to implement asynchronous
179 * closing and interruption for this channel. </p>
180 *
181 * @param completed
182 * {@code true} if, and only if, the I/O operation completed
183 * successfully, that is, had some effect that would be visible to
184 * the operation's invoker
185 *
186 * @throws AsynchronousCloseException
187 * If the channel was asynchronously closed
188 *
189 * @throws ClosedByInterruptException
190 * If the thread blocked in the I/O operation was interrupted
191 */
192 protected final void end(boolean completed)
193 throws AsynchronousCloseException
194 {
195 blockedOn(null);
196 Thread interrupted = this.interrupted;
197 if (interrupted != null && interrupted == Thread.currentThread()) {
198 this.interrupted = null;
199 throw new ClosedByInterruptException();
200 }
201 if (!completed && closed)
202 throw new AsynchronousCloseException();
203 }
204
205
206 // -- jdk.internal.misc.SharedSecrets --
207 static void blockedOn(Interruptible intr) { // package-private
208 SharedSecrets.getJavaLangAccess().blockedOn(intr);
209 }
210 }
211