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