src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java
changeset 47216 71c04702a3d5
parent 32143 394ab6a6658d
child 48761 74c1fa26435a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/nio/channels/spi/AbstractSelectableChannel.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.nio.channels.spi;
+
+import java.io.IOException;
+import java.nio.channels.*;
+
+
+/**
+ * Base implementation class for selectable channels.
+ *
+ * <p> This class defines methods that handle the mechanics of channel
+ * registration, deregistration, and closing.  It maintains the current
+ * blocking mode of this channel as well as its current set of selection keys.
+ * It performs all of the synchronization required to implement the {@link
+ * java.nio.channels.SelectableChannel} specification.  Implementations of the
+ * abstract protected methods defined in this class need not synchronize
+ * against other threads that might be engaged in the same operations.  </p>
+ *
+ *
+ * @author Mark Reinhold
+ * @author Mike McCloskey
+ * @author JSR-51 Expert Group
+ * @since 1.4
+ */
+
+public abstract class AbstractSelectableChannel
+    extends SelectableChannel
+{
+
+    // The provider that created this channel
+    private final SelectorProvider provider;
+
+    // Keys that have been created by registering this channel with selectors.
+    // They are saved because if this channel is closed the keys must be
+    // deregistered.  Protected by keyLock.
+    //
+    private SelectionKey[] keys = null;
+    private int keyCount = 0;
+
+    // Lock for key set and count
+    private final Object keyLock = new Object();
+
+    // Lock for registration and configureBlocking operations
+    private final Object regLock = new Object();
+
+    // Blocking mode, protected by regLock
+    boolean blocking = true;
+
+    /**
+     * Initializes a new instance of this class.
+     *
+     * @param  provider
+     *         The provider that created this channel
+     */
+    protected AbstractSelectableChannel(SelectorProvider provider) {
+        this.provider = provider;
+    }
+
+    /**
+     * Returns the provider that created this channel.
+     *
+     * @return  The provider that created this channel
+     */
+    public final SelectorProvider provider() {
+        return provider;
+    }
+
+
+    // -- Utility methods for the key set --
+
+    private void addKey(SelectionKey k) {
+        assert Thread.holdsLock(keyLock);
+        int i = 0;
+        if ((keys != null) && (keyCount < keys.length)) {
+            // Find empty element of key array
+            for (i = 0; i < keys.length; i++)
+                if (keys[i] == null)
+                    break;
+        } else if (keys == null) {
+            keys =  new SelectionKey[3];
+        } else {
+            // Grow key array
+            int n = keys.length * 2;
+            SelectionKey[] ks =  new SelectionKey[n];
+            for (i = 0; i < keys.length; i++)
+                ks[i] = keys[i];
+            keys = ks;
+            i = keyCount;
+        }
+        keys[i] = k;
+        keyCount++;
+    }
+
+    private SelectionKey findKey(Selector sel) {
+        synchronized (keyLock) {
+            if (keys == null)
+                return null;
+            for (int i = 0; i < keys.length; i++)
+                if ((keys[i] != null) && (keys[i].selector() == sel))
+                    return keys[i];
+            return null;
+        }
+    }
+
+    void removeKey(SelectionKey k) {                    // package-private
+        synchronized (keyLock) {
+            for (int i = 0; i < keys.length; i++)
+                if (keys[i] == k) {
+                    keys[i] = null;
+                    keyCount--;
+                }
+            ((AbstractSelectionKey)k).invalidate();
+        }
+    }
+
+    private boolean haveValidKeys() {
+        synchronized (keyLock) {
+            if (keyCount == 0)
+                return false;
+            for (int i = 0; i < keys.length; i++) {
+                if ((keys[i] != null) && keys[i].isValid())
+                    return true;
+            }
+            return false;
+        }
+    }
+
+
+    // -- Registration --
+
+    public final boolean isRegistered() {
+        synchronized (keyLock) {
+            return keyCount != 0;
+        }
+    }
+
+    public final SelectionKey keyFor(Selector sel) {
+        return findKey(sel);
+    }
+
+    /**
+     * Registers this channel with the given selector, returning a selection key.
+     *
+     * <p>  This method first verifies that this channel is open and that the
+     * given initial interest set is valid.
+     *
+     * <p> If this channel is already registered with the given selector then
+     * the selection key representing that registration is returned after
+     * setting its interest set to the given value.
+     *
+     * <p> Otherwise this channel has not yet been registered with the given
+     * selector, so the {@link AbstractSelector#register register} method of
+     * the selector is invoked while holding the appropriate locks.  The
+     * resulting key is added to this channel's key set before being returned.
+     * </p>
+     *
+     * @throws  ClosedSelectorException {@inheritDoc}
+     *
+     * @throws  IllegalBlockingModeException {@inheritDoc}
+     *
+     * @throws  IllegalSelectorException {@inheritDoc}
+     *
+     * @throws  CancelledKeyException {@inheritDoc}
+     *
+     * @throws  IllegalArgumentException {@inheritDoc}
+     */
+    public final SelectionKey register(Selector sel, int ops,
+                                       Object att)
+        throws ClosedChannelException
+    {
+        synchronized (regLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if ((ops & ~validOps()) != 0)
+                throw new IllegalArgumentException();
+            if (blocking)
+                throw new IllegalBlockingModeException();
+            SelectionKey k = findKey(sel);
+            if (k != null) {
+                k.interestOps(ops);
+                k.attach(att);
+            }
+            if (k == null) {
+                // New registration
+                synchronized (keyLock) {
+                    if (!isOpen())
+                        throw new ClosedChannelException();
+                    k = ((AbstractSelector)sel).register(this, ops, att);
+                    addKey(k);
+                }
+            }
+            return k;
+        }
+    }
+
+
+    // -- Closing --
+
+    /**
+     * Closes this channel.
+     *
+     * <p> This method, which is specified in the {@link
+     * AbstractInterruptibleChannel} class and is invoked by the {@link
+     * java.nio.channels.Channel#close close} method, in turn invokes the
+     * {@link #implCloseSelectableChannel implCloseSelectableChannel} method in
+     * order to perform the actual work of closing this channel.  It then
+     * cancels all of this channel's keys.  </p>
+     */
+    protected final void implCloseChannel() throws IOException {
+        implCloseSelectableChannel();
+        synchronized (keyLock) {
+            int count = (keys == null) ? 0 : keys.length;
+            for (int i = 0; i < count; i++) {
+                SelectionKey k = keys[i];
+                if (k != null)
+                    k.cancel();
+            }
+        }
+    }
+
+    /**
+     * Closes this selectable channel.
+     *
+     * <p> This method is invoked by the {@link java.nio.channels.Channel#close
+     * close} method in order to perform the actual work of closing the
+     * channel.  This method is only invoked if the channel has not yet been
+     * closed, and it is never invoked more than once.
+     *
+     * <p> An implementation of this method must arrange for any other thread
+     * that is blocked in an I/O operation upon this channel to return
+     * immediately, either by throwing an exception or by returning normally.
+     * </p>
+     *
+     * @throws  IOException
+     *          If an I/O error occurs
+     */
+    protected abstract void implCloseSelectableChannel() throws IOException;
+
+
+    // -- Blocking --
+
+    public final boolean isBlocking() {
+        synchronized (regLock) {
+            return blocking;
+        }
+    }
+
+    public final Object blockingLock() {
+        return regLock;
+    }
+
+    /**
+     * Adjusts this channel's blocking mode.
+     *
+     * <p> If the given blocking mode is different from the current blocking
+     * mode then this method invokes the {@link #implConfigureBlocking
+     * implConfigureBlocking} method, while holding the appropriate locks, in
+     * order to change the mode.  </p>
+     */
+    public final SelectableChannel configureBlocking(boolean block)
+        throws IOException
+    {
+        synchronized (regLock) {
+            if (!isOpen())
+                throw new ClosedChannelException();
+            if (blocking == block)
+                return this;
+            if (block && haveValidKeys())
+                throw new IllegalBlockingModeException();
+            implConfigureBlocking(block);
+            blocking = block;
+        }
+        return this;
+    }
+
+    /**
+     * Adjusts this channel's blocking mode.
+     *
+     * <p> This method is invoked by the {@link #configureBlocking
+     * configureBlocking} method in order to perform the actual work of
+     * changing the blocking mode.  This method is only invoked if the new mode
+     * is different from the current mode.  </p>
+     *
+     * @param  block  If {@code true} then this channel will be placed in
+     *                blocking mode; if {@code false} then it will be placed
+     *                non-blocking mode
+     *
+     * @throws IOException
+     *         If an I/O error occurs
+     */
+    protected abstract void implConfigureBlocking(boolean block)
+        throws IOException;
+
+}