/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE 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
* @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 <tt>true</tt> then this channel will be placed in
* blocking mode; if <tt>false</tt> then it will be placed
* non-blocking mode
*
* @throws IOException
* If an I/O error occurs
*/
protected abstract void
implConfigureBlocking(boolean
block)
throws
IOException;
}