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.access.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 true} if, 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 true} if, 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.access.SharedSecrets --
 207     static void blockedOn(Interruptible intr) {         // package-private
 208         SharedSecrets.getJavaLangAccess().blockedOn(intr);
 209     }
 210 }