OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / java / nio / channels / spi / AbstractInterruptibleChannel.java
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 package java.nio.channels.spi;
19
20 import java.io.IOException;
21 import java.lang.reflect.Method;
22 import java.nio.channels.AsynchronousCloseException;
23 import java.nio.channels.Channel;
24 import java.nio.channels.ClosedByInterruptException;
25 import java.nio.channels.InterruptibleChannel;
26 import java.security.AccessController;
27 import java.security.PrivilegedActionException;
28 import java.security.PrivilegedExceptionAction;
29
30 /**
31  * {@code AbstractInterruptibleChannel} is the root class for interruptible
32  * channels.
33  * <p>
34  * The basic usage pattern for an interruptible channel is to invoke
35  * {@code begin()} before any I/O operation that potentially blocks
36  * indefinitely, then {@code end(boolean)} after completing the operation. The
37  * argument to the {@code end} method should indicate if the I/O operation has
38  * actually completed so that any change may be visible to the invoker.
39 */
40 public abstract class AbstractInterruptibleChannel implements Channel,
41         InterruptibleChannel {
42
43     static Method setInterruptAction = null;
44
45     static {
46         try {
47             setInterruptAction = AccessController
48                     .doPrivileged(new PrivilegedExceptionAction<Method>() {
49                         public Method run() throws Exception {
50                             return Thread.class.getDeclaredMethod(
51                                     "setInterruptAction",
52                                     new Class[] { Runnable.class });
53
54                         }
55                     });
56             setInterruptAction.setAccessible(true);
57         } catch (PrivilegedActionException e) {
58             // FIXME: be accommodate before VM actually provides
59             // setInterruptAction method
60             // throw new Error(e);
61         }
62     }
63
64     private volatile boolean closed = false;
65
66     volatile boolean interrupted = false;
67
68     /**
69      * Default constructor.
70      */
71     protected AbstractInterruptibleChannel() {
72         super();
73     }
74
75     /**
76      * Indicates whether this channel is open.
77      *
78      * @return {@code true} if this channel is open, {@code false} if it is
79      *         closed.
80      * @see java.nio.channels.Channel#isOpen()
81      */
82     public synchronized final boolean isOpen() {
83         return !closed;
84     }
85
86     /**
87      * Closes an open channel. If the channel is already closed then this method
88      * has no effect, otherwise it closes the receiver via the
89      * {@code implCloseChannel} method.
90      * <p>
91      * If an attempt is made to perform an operation on a closed channel then a
92      * {@link java.nio.channels.ClosedChannelException} is thrown.
93      * <p>
94      * If multiple threads attempt to simultaneously close a channel, then only
95      * one thread will run the closure code and the others will be blocked until
96      * the first one completes.
97      *
98      * @throws IOException
99      *             if a problem occurs while closing this channel.
100      * @see java.nio.channels.Channel#close()
101      */
102     public final void close() throws IOException {
103         if (!closed) {
104             synchronized (this) {
105                 if (!closed) {
106                     closed = true;
107                     implCloseChannel();
108                 }
109             }
110         }
111     }
112
113     /**
114      * Indicates the beginning of a code section that includes an I/O operation
115      * that is potentially blocking. After this operation, the application
116      * should invoke the corresponding {@code end(boolean)} method.
117      */
118     protected final void begin() {
119         // FIXME: be accommodate before VM actually provides
120         // setInterruptAction method
121         if (setInterruptAction != null) {
122             try {
123                 setInterruptAction.invoke(Thread.currentThread(),
124                         new Object[] { new Runnable() {
125                             public void run() {
126                                 try {
127                                     interrupted = true;
128                                     AbstractInterruptibleChannel.this.close();
129                                 } catch (IOException e) {
130                                     // ignore
131                                 }
132                             }
133                         } });
134             } catch (Exception e) {
135                 throw new RuntimeException(e);
136             }
137         }
138     }
139
140     /**
141      * Indicates the end of a code section that has been started with
142      * {@code begin()} and that includes a potentially blocking I/O operation.
143      *
144      * @param success
145      *            pass {@code true} if the blocking operation has succeeded and
146      *            has had a noticeable effect; {@code false} otherwise.
147      * @throws AsynchronousCloseException
148      *             if this channel is closed by another thread while this method
149      *             is executing.
150      * @throws ClosedByInterruptException
151      *             if another thread interrupts the calling thread while this
152      *             method is executing.
153      */
154     protected final void end(boolean success) throws AsynchronousCloseException {
155         // FIXME: be accommodate before VM actually provides
156         // setInterruptAction method
157         if (setInterruptAction != null) {
158             try {
159                 setInterruptAction.invoke(Thread.currentThread(),
160                         new Object[] { null });
161             } catch (Exception e) {
162                 throw new RuntimeException(e);
163             }
164             if (interrupted) {
165                 interrupted = false;
166                 throw new ClosedByInterruptException();
167             }
168         }
169         if (!success && closed) {
170             throw new AsynchronousCloseException();
171         }
172     }
173
174     /**
175      * Implements the channel closing behavior.
176      * <p>
177      * Closes the channel with a guarantee that the channel is not currently
178      * closed through another invocation of {@code close()} and that the method
179      * is thread-safe.
180      * <p>
181      * Any outstanding threads blocked on I/O operations on this channel must be
182      * released with either a normal return code, or by throwing an
183      * {@code AsynchronousCloseException}.
184      *
185      * @throws IOException
186      *             if a problem occurs while closing the channel.
187      */
188     protected abstract void implCloseChannel() throws IOException;
189 }