/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.harmony.nio.internal;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.IllegalSelectorException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.AbstractSelectionKey;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.harmony.luni.platform.FileDescriptorHandler;
import org.apache.harmony.luni.platform.Platform;
/*
* Default implementation of java.nio.channels.Selector
*
*/
final class SelectorImpl extends AbstractSelector {
private static final int MOCK_WRITEBUF_SIZE = 1;
private static final int MOCK_READBUF_SIZE = 8;
private static final int NA = 0;
private static final int READABLE = 1;
private static final int WRITEABLE = 2;
private static final int SELECT_BLOCK = -1;
private static final int SELECT_NOW = 0;
// keysLock is used to brief synchronization when get selectionKeys snapshot
// before selection
final Object keysLock = new Object();
private final Set<SelectionKey> keys = new HashSet<SelectionKey>();
private Set<SelectionKey> unmodifiableKeys = Collections
.unmodifiableSet(keys);
private final Set<SelectionKey> selectedKeys = new HashSet<SelectionKey>();
private Set<SelectionKey> unaddableSelectedKeys = new UnaddableSet<SelectionKey>(
selectedKeys);
// sink and source are used by wakeup()
private Pipe.SinkChannel sink;
private Pipe.SourceChannel source;
private FileDescriptor sourcefd;
private SelectionKey[] readableChannels;
private SelectionKey[] writableChannels;
private List<FileDescriptor> readableFDs = new ArrayList<FileDescriptor>();
private List<FileDescriptor> writableFDs = new ArrayList<FileDescriptor>();
private FileDescriptor[] readable;
private FileDescriptor[] writable;
public SelectorImpl(SelectorProvider selectorProvider) {
super(selectorProvider);
try {
Pipe mockSelector = selectorProvider.openPipe();
sink = mockSelector.sink();
source = mockSelector.source();
sourcefd = ((FileDescriptorHandler)source).getFD();
source.configureBlocking(false);
} catch (IOException e) {
// do nothing
}
}
/*
* @see java.nio.channels.spi.AbstractSelector#implCloseSelector()
*/
protected void implCloseSelector() throws IOException {
doCancel();
for (SelectionKey sk : keys) {
deregister((AbstractSelectionKey) sk);
}
wakeup();
}
/*
* @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel,
* int, java.lang.Object)
*/
protected SelectionKey register(AbstractSelectableChannel channel,
int operations, Object attachment) {
if (!provider().equals(channel.provider())) {
throw new IllegalSelectorException();
}
synchronized (this) {
synchronized (keys) {
SelectionKey sk = new SelectionKeyImpl(channel, operations,
attachment, this);
keys.add(sk);
return sk;
}
}
}
/*
* @see java.nio.channels.Selector#keys()
*/
public synchronized Set<SelectionKey> keys() {
closeCheck();
return unmodifiableKeys;
}
private void closeCheck() {
if (!isOpen()) {
throw new ClosedSelectorException();
}
}
/*
* @see java.nio.channels.Selector#select()
*/
public int select() throws IOException {
return selectInternal(SELECT_BLOCK);
}
/*
* @see java.nio.channels.Selector#select(long)
*/
public int select(long timeout) throws IOException {
if (timeout < 0) {
throw new IllegalArgumentException();
}
return selectInternal((0 == timeout) ? SELECT_BLOCK : timeout);
}
/*
* @see java.nio.channels.Selector#selectNow()
*/
public int selectNow() throws IOException {
return selectInternal(SELECT_NOW);
}
private int selectInternal(long timeout) throws IOException {
closeCheck();
synchronized (this) {
synchronized (keys) {
synchronized (selectedKeys) {
doCancel();
int[] readyChannels = null;
boolean isBlock = (SELECT_NOW != timeout);
// BEGIN android-removed
// copied from newer version of harmony
// if (keys.size() == 0) {
// return 0;
// }
// END android-removed
prepareChannels();
try {
if (isBlock) {
begin();
}
readyChannels = Platform.getNetworkSystem().select(readable, writable, timeout);
} finally {
// clear results for next select
readableFDs.clear();
writableFDs.clear();
if (isBlock) {
end();
}
}
return processSelectResult(readyChannels);
}
}
}
}
private boolean isConnected(SelectionKeyImpl key) {
SelectableChannel channel = key.channel();
if (channel instanceof SocketChannel) {
return ((SocketChannel) channel).isConnected();
}
return true;
}
// Prepares and adds channels to list for selection
private void prepareChannels() {
readableFDs.add(sourcefd);
List<SelectionKey> readChannelList = new ArrayList<SelectionKey>();
readChannelList.add(source.keyFor(this));
List<SelectionKey> writeChannelList = new ArrayList<SelectionKey>();
synchronized (keysLock) {
for (Iterator<SelectionKey> i = keys.iterator(); i.hasNext();) {
SelectionKeyImpl key = (SelectionKeyImpl) i.next();
key.oldInterestOps = key.interestOps();
boolean isReadableChannel = ((SelectionKey.OP_ACCEPT | SelectionKey.OP_READ) & key.oldInterestOps) != 0;
boolean isWritableChannel = ((SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE) & key.oldInterestOps) != 0;
SelectableChannel channel = key.channel();
if (isReadableChannel) {
readChannelList.add(channel.keyFor(this));
readableFDs.add(((FileDescriptorHandler)channel).getFD());
}
if (isWritableChannel) {
writeChannelList.add(channel.keyFor(this));
writableFDs.add(((FileDescriptorHandler)channel).getFD());
}
}
}
readableChannels = readChannelList.toArray(new SelectionKey[0]);
writableChannels = writeChannelList.toArray(new SelectionKey[0]);
readable = readableFDs.toArray(new FileDescriptor[0]);
writable = writableFDs.toArray(new FileDescriptor[0]);
}
// Analyses selected channels and adds keys of ready channels to
// selectedKeys list
private int processSelectResult(int[] readyChannels) throws IOException {
if (0 == readyChannels.length) {
return 0;
}
// if the mock channel is selected, read the content.
if (READABLE == readyChannels[0]) {
ByteBuffer readbuf = ByteBuffer.allocate(MOCK_READBUF_SIZE);
while (source.read(readbuf) > 0) {
readbuf.flip();
}
}
int selected = 0;
for (int i = 1; i < readyChannels.length; i++) {
SelectionKeyImpl key = (SelectionKeyImpl) (i >= readable.length ? writableChannels[i
- readable.length]
: readableChannels[i]);
if (null == key) {
continue;
}
boolean isOldSelectedKey = selectedKeys.contains(key);
int selectedOp = 0;
// set ready ops
switch (readyChannels[i]) {
case NA:
selectedOp = 0;
break;
case READABLE:
selectedOp = (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)
& key.oldInterestOps;
break;
case WRITEABLE:
if (isConnected(key)) {
selectedOp = SelectionKey.OP_WRITE & key.oldInterestOps;
} else {
selectedOp = SelectionKey.OP_CONNECT & key.oldInterestOps;
}
break;
}
if (0 != selectedOp) {
if (isOldSelectedKey && key.readyOps() != selectedOp) {
key.setReadyOps(key.readyOps() | selectedOp);
selected++;
} else if (!isOldSelectedKey) {
key.setReadyOps(selectedOp);
selectedKeys.add(key);
selected++;
}
}
}
readableChannels = null;
writableChannels = null;
return selected;
}
/*
* @see java.nio.channels.Selector#selectedKeys()
*/
public synchronized Set<SelectionKey> selectedKeys() {
closeCheck();
return unaddableSelectedKeys;
}
private void doCancel() {
Set<SelectionKey> cancelledKeys = cancelledKeys();
synchronized (cancelledKeys) {
if (cancelledKeys.size() > 0) {
for (SelectionKey currentkey : cancelledKeys) {
deregister((AbstractSelectionKey) currentkey);
keys.remove(currentkey);
selectedKeys.remove(currentkey);
}
}
cancelledKeys.clear();
}
}
/*
* @see java.nio.channels.Selector#wakeup()
*/
public Selector wakeup() {
try {
sink.write(ByteBuffer.allocate(MOCK_WRITEBUF_SIZE));
} catch (IOException e) {
// do nothing
}
return this;
}
private static class UnaddableSet<E> implements Set<E> {
private Set<E> set;
UnaddableSet(Set<E> set) {
this.set = set;
}
public boolean equals(Object object) {
return set.equals(object);
}
public int hashCode() {
return set.hashCode();
}
public boolean add(E object) {
throw new UnsupportedOperationException();
}
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
public void clear() {
set.clear();
}
public boolean contains(Object object) {
return set.contains(object);
}
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public boolean isEmpty() {
return set.isEmpty();
}
public Iterator<E> iterator() {
return set.iterator();
}
public boolean remove(Object object) {
return set.remove(object);
}
public boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
public int size() {
return set.size();
}
public Object[] toArray() {
return set.toArray();
}
public <T> T[] toArray(T[] a) {
return set.toArray(a);
}
}
}
|