src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java
changeset 47216 71c04702a3d5
parent 47004 b7e72fc752c9
child 49001 ce06058197a4
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2000, 2017, 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.misc.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.misc.SharedSecrets --
       
   207     static void blockedOn(Interruptible intr) {         // package-private
       
   208         SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), intr);
       
   209     }
       
   210 }