--- /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;
+
+}