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