FileDocCategorySizeDatePackage
AbstractSelectableChannel.javaAPI DocJava SE 5 API7997Fri Aug 26 14:57:10 BST 2005java.nio.channels.spi

AbstractSelectableChannel.java

/*
 * @(#)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.
 *
 * <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
 * @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.
     *
     * <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>
     */
    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.
     *
     * <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>
     */
    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
    {
        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.
     *
     * <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>
     *
     * @throws IOException
     *         If an I/O error occurs
     */
    protected abstract void implConfigureBlocking(boolean block)
	throws IOException;

}