1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.  Oracle designates this
9  * particular file as subject to the "Classpath" exception as provided
10  * by Oracle in the LICENSE file that accompanied this code.
11  *
12  * This code is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15  * version 2 for more details (a copy is included in the LICENSE file that
16  * accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License version
19  * 2 along with this work; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23  * or visit www.oracle.com if you need additional information or have any
24  * questions.
25  */
26 
27 /*
28  */
29 
30 package java.nio.channels.spi;
31 
32 import java.io.IOException;
33 import java.nio.channels.*;
34 import jdk.internal.access.SharedSecrets;
35 import sun.nio.ch.Interruptible;
36 
37 
38 /**
39  * Base implementation class for interruptible channels.
40  *
41  * <p> This class encapsulates the low-level machinery required to implement
42  * the asynchronous closing and interruption of channels.  A concrete channel
43  * class must invoke the {@link #begin begin} and {@link #end end} methods
44  * before and after, respectively, invoking an I/O operation that might block
45  * indefinitely.  In order to ensure that the {@link #end end} method is always
46  * invoked, these methods should be used within a
47  * {@code try}&nbsp;...&nbsp;{@code finally} block:
48  *
49  * <blockquote><pre id="be">
50  * boolean completed = false;
51  * try {
52  *     begin();
53  *     completed = ...;    // Perform blocking I/O operation
54  *     return ...;         // Return result
55  * } finally {
56  *     end(completed);
57  * }</pre></blockquote>
58  *
59  * <p> The {@code completed} argument to the {@link #end end} method tells
60  * whether or not the I/O operation actually completed, that is, whether it had
61  * any effect that would be visible to the invoker.  In the case of an
62  * operation that reads bytes, for example, this argument should be
63  * {@code true} if, and only if, some bytes were actually transferred into the
64  * invoker's target buffer.
65  *
66  * <p> A concrete channel class must also implement the {@link
67  * #implCloseChannel implCloseChannel} method in such a way that if it is
68  * invoked while another thread is blocked in a native I/O operation upon the
69  * channel then that operation will immediately return, either by throwing an
70  * exception or by returning normally.  If a thread is interrupted or the
71  * channel upon which it is blocked is asynchronously closed then the channel's
72  * {@link #end end} method will throw the appropriate exception.
73  *
74  * <p> This class performs the synchronization required to implement the {@link
75  * java.nio.channels.Channel} specification.  Implementations of the {@link
76  * #implCloseChannel implCloseChannel} method need not synchronize against
77  * other threads that might be attempting to close the channel.  </p>
78  *
79  *
80  * @author Mark Reinhold
81  * @author JSR-51 Expert Group
82  * @since 1.4
83  */
84 
85 public abstract class AbstractInterruptibleChannel
86     implements Channel, InterruptibleChannel
87 {
88 
89     private final Object closeLock = new Object();
90     private volatile boolean closed;
91 
92     /**
93      * Initializes a new instance of this class.
94      */
AbstractInterruptibleChannel()95     protected AbstractInterruptibleChannel() { }
96 
97     /**
98      * Closes this channel.
99      *
100      * <p> If the channel has already been closed then this method returns
101      * immediately.  Otherwise it marks the channel as closed and then invokes
102      * the {@link #implCloseChannel implCloseChannel} method in order to
103      * complete the close operation.  </p>
104      *
105      * @throws  IOException
106      *          If an I/O error occurs
107      */
close()108     public final void close() throws IOException {
109         synchronized (closeLock) {
110             if (closed)
111                 return;
112             closed = true;
113             implCloseChannel();
114         }
115     }
116 
117     /**
118      * Closes this channel.
119      *
120      * <p> This method is invoked by the {@link #close close} method in order
121      * to perform the actual work of closing the channel.  This method is only
122      * invoked if the channel has not yet been closed, and it is never invoked
123      * more than once.
124      *
125      * <p> An implementation of this method must arrange for any other thread
126      * that is blocked in an I/O operation upon this channel to return
127      * immediately, either by throwing an exception or by returning normally.
128      * </p>
129      *
130      * @throws  IOException
131      *          If an I/O error occurs while closing the channel
132      */
implCloseChannel()133     protected abstract void implCloseChannel() throws IOException;
134 
isOpen()135     public final boolean isOpen() {
136         return !closed;
137     }
138 
139 
140     // -- Interruption machinery --
141 
142     private Interruptible interruptor;
143     private volatile Thread interrupted;
144 
145     /**
146      * Marks the beginning of an I/O operation that might block indefinitely.
147      *
148      * <p> This method should be invoked in tandem with the {@link #end end}
149      * method, using a {@code try}&nbsp;...&nbsp;{@code finally} block as
150      * shown <a href="#be">above</a>, in order to implement asynchronous
151      * closing and interruption for this channel.  </p>
152      */
begin()153     protected final void begin() {
154         if (interruptor == null) {
155             interruptor = new Interruptible() {
156                     public void interrupt(Thread target) {
157                         synchronized (closeLock) {
158                             if (closed)
159                                 return;
160                             closed = true;
161                             interrupted = target;
162                             try {
163                                 AbstractInterruptibleChannel.this.implCloseChannel();
164                             } catch (IOException x) { }
165                         }
166                     }};
167         }
168         blockedOn(interruptor);
169         Thread me = Thread.currentThread();
170         if (me.isInterrupted())
171             interruptor.interrupt(me);
172     }
173 
174     /**
175      * Marks the end of an I/O operation that might block indefinitely.
176      *
177      * <p> This method should be invoked in tandem with the {@link #begin
178      * begin} method, using a {@code try}&nbsp;...&nbsp;{@code finally} block
179      * as shown <a href="#be">above</a>, in order to implement asynchronous
180      * closing and interruption for this channel.  </p>
181      *
182      * @param  completed
183      *         {@code true} if, and only if, the I/O operation completed
184      *         successfully, that is, had some effect that would be visible to
185      *         the operation's invoker
186      *
187      * @throws  AsynchronousCloseException
188      *          If the channel was asynchronously closed
189      *
190      * @throws  ClosedByInterruptException
191      *          If the thread blocked in the I/O operation was interrupted
192      */
end(boolean completed)193     protected final void end(boolean completed)
194         throws AsynchronousCloseException
195     {
196         blockedOn(null);
197         Thread interrupted = this.interrupted;
198         if (interrupted != null && interrupted == Thread.currentThread()) {
199             this.interrupted = null;
200             throw new ClosedByInterruptException();
201         }
202         if (!completed && closed)
203             throw new AsynchronousCloseException();
204     }
205 
206 
207     // -- jdk.internal.misc.SharedSecrets --
blockedOn(Interruptible intr)208     static void blockedOn(Interruptible intr) {         // package-private
209         // Android-changed: Call Thread.currentThread().blockedOn() directly.
210         // SharedSecrets.getJavaLangAccess().blockedOn(intr);
211         Thread.currentThread().blockedOn(intr);
212     }
213 }
214