• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.nio.channels.AsynchronousCloseException;
22 import java.nio.channels.Channel;
23 import java.nio.channels.ClosedByInterruptException;
24 import java.nio.channels.InterruptibleChannel;
25 
26 /**
27  * {@code AbstractInterruptibleChannel} is the root class for interruptible
28  * channels.
29  * <p>
30  * The basic usage pattern for an interruptible channel is to invoke
31  * {@code begin()} before any I/O operation that potentially blocks
32  * indefinitely, then {@code end(boolean)} after completing the operation. The
33  * argument to the {@code end} method should indicate if the I/O operation has
34  * actually completed so that any change may be visible to the invoker.
35 */
36 public abstract class AbstractInterruptibleChannel implements Channel, InterruptibleChannel {
37 
38     private volatile boolean closed = false;
39 
40     volatile boolean interrupted = false;
41 
42     private final Runnable interruptAndCloseRunnable = new Runnable() {
43         @Override public void run() {
44             try {
45                 interrupted = true;
46                 AbstractInterruptibleChannel.this.close();
47             } catch (IOException ignored) {
48             }
49         }
50     };
51 
AbstractInterruptibleChannel()52     protected AbstractInterruptibleChannel() {
53     }
54 
isOpen()55     @Override public synchronized final boolean isOpen() {
56         return !closed;
57     }
58 
59     /**
60      * Closes an open channel. If the channel is already closed then this method
61      * has no effect, otherwise it closes the receiver via the
62      * {@code implCloseChannel} method.
63      * <p>
64      * If an attempt is made to perform an operation on a closed channel then a
65      * {@link java.nio.channels.ClosedChannelException} is thrown.
66      * <p>
67      * If multiple threads attempt to simultaneously close a channel, then only
68      * one thread will run the closure code and the others will be blocked until
69      * the first one completes.
70      *
71      * @throws IOException
72      *             if a problem occurs while closing this channel.
73      * @see java.nio.channels.Channel#close()
74      */
close()75     @Override public final void close() throws IOException {
76         if (!closed) {
77             synchronized (this) {
78                 if (!closed) {
79                     closed = true;
80                     implCloseChannel();
81                 }
82             }
83         }
84     }
85 
86     /**
87      * Indicates the beginning of a code section that includes an I/O operation
88      * that is potentially blocking. After this operation, the application
89      * should invoke the corresponding {@code end(boolean)} method.
90      */
begin()91     protected final void begin() {
92         Thread.currentThread().pushInterruptAction$(interruptAndCloseRunnable);
93     }
94 
95     /**
96      * Indicates the end of a code section that has been started with
97      * {@code begin()} and that includes a potentially blocking I/O operation.
98      *
99      * @param success
100      *            pass {@code true} if the blocking operation has succeeded and
101      *            has had a noticeable effect; {@code false} otherwise.
102      * @throws AsynchronousCloseException
103      *             if this channel is closed by another thread while this method
104      *             is executing.
105      * @throws ClosedByInterruptException
106      *             if another thread interrupts the calling thread while this
107      *             method is executing.
108      */
end(boolean success)109     protected final void end(boolean success) throws AsynchronousCloseException {
110         Thread.currentThread().popInterruptAction$(interruptAndCloseRunnable);
111         if (interrupted) {
112             interrupted = false;
113             throw new ClosedByInterruptException();
114         }
115         if (!success && closed) {
116             throw new AsynchronousCloseException();
117         }
118     }
119 
120     /**
121      * Implements the channel closing behavior.
122      * <p>
123      * Closes the channel with a guarantee that the channel is not currently
124      * closed through another invocation of {@code close()} and that the method
125      * is thread-safe.
126      * <p>
127      * Any outstanding threads blocked on I/O operations on this channel must be
128      * released with either a normal return code, or by throwing an
129      * {@code AsynchronousCloseException}.
130      *
131      * @throws IOException
132      *             if a problem occurs while closing the channel.
133      */
implCloseChannel()134     protected abstract void implCloseChannel() throws IOException;
135 }
136