/* * @(#)AbstractSelectableChannel.java 1.25 03/12/19 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.nio.channels.spi; import java.io.IOException; import java.nio.channels.*; /** * Base implementation class for selectable channels. * *
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.
* * * @author Mark Reinhold * @author Mike McCloskey * @author JSR-51 Expert Group * @version 1.25, 03/12/19 * @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. */ 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) { synchronized (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. * *This method first verifies that this channel is open and that the * given initial interest set is valid. * *
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. * *
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. *
*/ public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { if (!isOpen()) throw new ClosedChannelException(); if ((ops & ~validOps()) != 0) throw new IllegalArgumentException(); synchronized (regLock) { if (blocking) throw new IllegalBlockingModeException(); SelectionKey k = findKey(sel); if (k != null) { k.interestOps(ops); k.attach(att); } if (k == null) { // New registration k = ((AbstractSelector)sel).register(this, ops, att); addKey(k); } return k; } } // -- Closing -- /** * Closes this channel. * *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.
*/ 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. * *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. * *
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. *
*/ 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. * *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.
*/ public final SelectableChannel configureBlocking(boolean block) throws IOException { if (!isOpen()) throw new ClosedChannelException(); synchronized (regLock) { if (blocking == block) return this; if (block && haveValidKeys()) throw new IllegalBlockingModeException(); implConfigureBlocking(block); blocking = block; } return this; } /** * Adjusts this channel's blocking mode. * *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.
* * @throws IOException * If an I/O error occurs */ protected abstract void implConfigureBlocking(boolean block) throws IOException; }