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 package java.nio.channels.spi;
  27 
  28 import java.io.IOException;
  29 import java.nio.channels.CancelledKeyException;
  30 import java.nio.channels.ClosedChannelException;
  31 import java.nio.channels.ClosedSelectorException;
  32 import java.nio.channels.IllegalBlockingModeException;
  33 import java.nio.channels.IllegalSelectorException;
  34 import java.nio.channels.SelectableChannel;
  35 import java.nio.channels.SelectionKey;
  36 import java.nio.channels.Selector;
  37 
  38 
  39 /**
  40  * Base implementation class for selectable channels.
  41  *
  42  * <p> This class defines methods that handle the mechanics of channel
  43  * registration, deregistration, and closing.  It maintains the current
  44  * blocking mode of this channel as well as its current set of selection keys.
  45  * It performs all of the synchronization required to implement the {@link
  46  * java.nio.channels.SelectableChannel} specification.  Implementations of the
  47  * abstract protected methods defined in this class need not synchronize
  48  * against other threads that might be engaged in the same operations.  </p>
  49  *
  50  *
  51  * @author Mark Reinhold
  52  * @author Mike McCloskey
  53  * @author JSR-51 Expert Group
  54  * @since 1.4
  55  */
  56 
  57 public abstract class AbstractSelectableChannel
  58     extends SelectableChannel
  59 {
  60 
  61     // The provider that created this channel
  62     private final SelectorProvider provider;
  63 
  64     // Keys that have been created by registering this channel with selectors.
  65     // They are saved because if this channel is closed the keys must be
  66     // deregistered.  Protected by keyLock.
  67     //
  68     private SelectionKey[] keys = null;
  69     private int keyCount = 0;
  70 
  71     // Lock for key set and count
  72     private final Object keyLock = new Object();
  73 
  74     // Lock for registration and configureBlocking operations
  75     private final Object regLock = new Object();
  76 
  77     // Blocking mode, need regLock to change;
  78     private volatile boolean nonBlocking;
  79 
  80     /**
  81      * Initializes a new instance of this class.
  82      *
  83      * @param  provider
  84      *         The provider that created this channel
  85      */
  86     protected AbstractSelectableChannel(SelectorProvider provider) {
  87         this.provider = provider;
  88     }
  89 
  90     /**
  91      * Returns the provider that created this channel.
  92      *
  93      * @return  The provider that created this channel
  94      */
  95     public final SelectorProvider provider() {
  96         return provider;
  97     }
  98 
  99 
 100     // -- Utility methods for the key set --
 101 
 102     private void addKey(SelectionKey k) {
 103         assert Thread.holdsLock(keyLock);
 104         int i = 0;
 105         if ((keys != null) && (keyCount < keys.length)) {
 106             // Find empty element of key array
 107             for (i = 0; i < keys.length; i++)
 108                 if (keys[i] == null)
 109                     break;
 110         } else if (keys == null) {
 111             keys =  new SelectionKey[3];
 112         } else {
 113             // Grow key array
 114             int n = keys.length * 2;
 115             SelectionKey[] ks =  new SelectionKey[n];
 116             for (i = 0; i < keys.length; i++)
 117                 ks[i] = keys[i];
 118             keys = ks;
 119             i = keyCount;
 120         }
 121         keys[i] = k;
 122         keyCount++;
 123     }
 124 
 125     private SelectionKey findKey(Selector sel) {
 126         synchronized (keyLock) {
 127             if (keys == null)
 128                 return null;
 129             for (int i = 0; i < keys.length; i++)
 130                 if ((keys[i] != null) && (keys[i].selector() == sel))
 131                     return keys[i];
 132             return null;
 133         }
 134     }
 135 
 136     void removeKey(SelectionKey k) {                    // package-private
 137         synchronized (keyLock) {
 138             for (int i = 0; i < keys.length; i++)
 139                 if (keys[i] == k) {
 140                     keys[i] = null;
 141                     keyCount--;
 142                 }
 143             ((AbstractSelectionKey)k).invalidate();
 144         }
 145     }
 146 
 147     private boolean haveValidKeys() {
 148         synchronized (keyLock) {
 149             if (keyCount == 0)
 150                 return false;
 151             for (int i = 0; i < keys.length; i++) {
 152                 if ((keys[i] != null) && keys[i].isValid())
 153                     return true;
 154             }
 155             return false;
 156         }
 157     }
 158 
 159 
 160     // -- Registration --
 161 
 162     public final boolean isRegistered() {
 163         synchronized (keyLock) {
 164             return keyCount != 0;
 165         }
 166     }
 167 
 168     public final SelectionKey keyFor(Selector sel) {
 169         return findKey(sel);
 170     }
 171 
 172     /**
 173      * Registers this channel with the given selector, returning a selection key.
 174      *
 175      * <p>  This method first verifies that this channel is open and that the
 176      * given initial interest set is valid.
 177      *
 178      * <p> If this channel is already registered with the given selector then
 179      * the selection key representing that registration is returned after
 180      * setting its interest set to the given value.
 181      *
 182      * <p> Otherwise this channel has not yet been registered with the given
 183      * selector, so the {@link AbstractSelector#register register} method of
 184      * the selector is invoked while holding the appropriate locks.  The
 185      * resulting key is added to this channel's key set before being returned.
 186      * </p>
 187      *
 188      * @throws  ClosedSelectorException {@inheritDoc}
 189      *
 190      * @throws  IllegalBlockingModeException {@inheritDoc}
 191      *
 192      * @throws  IllegalSelectorException {@inheritDoc}
 193      *
 194      * @throws  CancelledKeyException {@inheritDoc}
 195      *
 196      * @throws  IllegalArgumentException {@inheritDoc}
 197      */
 198     public final SelectionKey register(Selector sel, int ops,
 199                                        Object att)
 200         throws ClosedChannelException
 201     {
 202         synchronized (regLock) {
 203             if (!isOpen())
 204                 throw new ClosedChannelException();
 205             if ((ops & ~validOps()) != 0)
 206                 throw new IllegalArgumentException();
 207             if (isBlocking())
 208                 throw new IllegalBlockingModeException();
 209             SelectionKey k = findKey(sel);
 210             if (k != null) {
 211                 k.interestOps(ops);
 212                 k.attach(att);
 213             }
 214             if (k == null) {
 215                 // New registration
 216                 synchronized (keyLock) {
 217                     if (!isOpen())
 218                         throw new ClosedChannelException();
 219                     k = ((AbstractSelector)sel).register(this, ops, att);
 220                     addKey(k);
 221                 }
 222             }
 223             return k;
 224         }
 225     }
 226 
 227 
 228     // -- Closing --
 229 
 230     /**
 231      * Closes this channel.
 232      *
 233      * <p> This method, which is specified in the {@link
 234      * AbstractInterruptibleChannel} class and is invoked by the {@link
 235      * java.nio.channels.Channel#close close} method, in turn invokes the
 236      * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
 237      * order to perform the actual work of closing this channel.  It then
 238      * cancels all of this channel's keys.  </p>
 239      */
 240     protected final void implCloseChannel() throws IOException {
 241         implCloseSelectableChannel();
 242         synchronized (keyLock) {
 243             int count = (keys == null) ? 0 : keys.length;
 244             for (int i = 0; i < count; i++) {
 245                 SelectionKey k = keys[i];
 246                 if (k != null)
 247                     k.cancel();
 248             }
 249         }
 250     }
 251 
 252     /**
 253      * Closes this selectable channel.
 254      *
 255      * <p> This method is invoked by the {@link java.nio.channels.Channel#close
 256      * close} method in order to perform the actual work of closing the
 257      * channel.  This method is only invoked if the channel has not yet been
 258      * closed, and it is never invoked more than once.
 259      *
 260      * <p> An implementation of this method must arrange for any other thread
 261      * that is blocked in an I/O operation upon this channel to return
 262      * immediately, either by throwing an exception or by returning normally.
 263      * </p>
 264      *
 265      * @throws  IOException
 266      *          If an I/O error occurs
 267      */
 268     protected abstract void implCloseSelectableChannel() throws IOException;
 269 
 270 
 271     // -- Blocking --
 272 
 273     public final boolean isBlocking() {
 274         return !nonBlocking;
 275     }
 276 
 277     public final Object blockingLock() {
 278         return regLock;
 279     }
 280 
 281     /**
 282      * Adjusts this channel's blocking mode.
 283      *
 284      * <p> If the given blocking mode is different from the current blocking
 285      * mode then this method invokes the {@link #implConfigureBlocking
 286      * implConfigureBlocking} method, while holding the appropriate locks, in
 287      * order to change the mode.  </p>
 288      */
 289     public final SelectableChannel configureBlocking(boolean block)
 290         throws IOException
 291     {
 292         synchronized (regLock) {
 293             if (!isOpen())
 294                 throw new ClosedChannelException();
 295             boolean blocking = !nonBlocking;
 296             if (block != blocking) {
 297                 if (block && haveValidKeys())
 298                     throw new IllegalBlockingModeException();
 299                 implConfigureBlocking(block);
 300                 nonBlocking = !block;
 301             }
 302         }
 303         return this;
 304     }
 305 
 306     /**
 307      * Adjusts this channel's blocking mode.
 308      *
 309      * <p> This method is invoked by the {@link #configureBlocking
 310      * configureBlocking} method in order to perform the actual work of
 311      * changing the blocking mode.  This method is only invoked if the new mode
 312      * is different from the current mode.  </p>
 313      *
 314      * @param  block  If {@code true} then this channel will be placed in
 315      *                blocking mode; if {@code false} then it will be placed
 316      *                non-blocking mode
 317      *
 318      * @throws IOException
 319      *         If an I/O error occurs
 320      */
 321     protected abstract void implConfigureBlocking(boolean block)
 322         throws IOException;
 323 
 324 }