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}&nbsp;...&nbsp;{@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 trueif, 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}&nbsp;...&nbsp;{@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}&nbsp;...&nbsp;{@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 trueif, 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