325 lines
11 KiB
Java
325 lines
11 KiB
Java
/*
|
|
* Copyright (c) 2002, 2007, 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.
|
|
*/
|
|
|
|
// Parts Copyright (C) 2002-2007 Jeroen Frijters
|
|
|
|
package sun.nio.ch;
|
|
|
|
import cli.System.Net.Sockets.Socket;
|
|
import cli.System.Net.Sockets.SocketException;
|
|
import cli.System.Net.Sockets.AddressFamily;
|
|
import cli.System.Net.Sockets.SocketType;
|
|
import cli.System.Net.Sockets.ProtocolType;
|
|
import cli.System.Net.Sockets.SelectMode;
|
|
import cli.System.Collections.ArrayList;
|
|
import java.io.IOException;
|
|
import java.nio.channels.ClosedSelectorException;
|
|
import java.nio.channels.Pipe;
|
|
import java.nio.channels.SelectableChannel;
|
|
import java.nio.channels.SelectionKey;
|
|
import java.nio.channels.Selector;
|
|
import java.nio.channels.spi.AbstractSelectableChannel;
|
|
import java.nio.channels.spi.AbstractSelector;
|
|
import java.nio.channels.spi.SelectorProvider;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Set;
|
|
|
|
final class DotNetSelectorImpl extends SelectorImpl
|
|
{
|
|
private ArrayList channelArray = new ArrayList();
|
|
private long updateCount = 0;
|
|
|
|
//Pipe used as a wakeup object.
|
|
private final Pipe wakeupPipe;
|
|
|
|
// File descriptors corresponding to source and sink
|
|
private final Socket wakeupSourceFd, wakeupSinkFd;
|
|
|
|
// Lock for interrupt triggering and clearing
|
|
private final Object interruptLock = new Object();
|
|
private volatile boolean interruptTriggered = false;
|
|
|
|
// class for fdMap entries
|
|
private final static class MapEntry
|
|
{
|
|
SelectionKeyImpl ski;
|
|
long updateCount = 0;
|
|
long clearedCount = 0;
|
|
MapEntry(SelectionKeyImpl ski)
|
|
{
|
|
this.ski = ski;
|
|
}
|
|
}
|
|
private final HashMap<Socket, MapEntry> fdMap = new HashMap<Socket, MapEntry>();
|
|
|
|
DotNetSelectorImpl(SelectorProvider sp) throws IOException
|
|
{
|
|
super(sp);
|
|
wakeupPipe = Pipe.open();
|
|
wakeupSourceFd = ((SelChImpl)wakeupPipe.source()).getFD().getSocket();
|
|
|
|
// Disable the Nagle algorithm so that the wakeup is more immediate
|
|
SinkChannelImpl sink = (SinkChannelImpl)wakeupPipe.sink();
|
|
(sink.sc).socket().setTcpNoDelay(true);
|
|
wakeupSinkFd = ((SelChImpl)sink).getFD().getSocket();
|
|
}
|
|
|
|
protected int doSelect(long timeout) throws IOException
|
|
{
|
|
if (channelArray == null)
|
|
throw new ClosedSelectorException();
|
|
processDeregisterQueue();
|
|
if (interruptTriggered)
|
|
{
|
|
resetWakeupSocket();
|
|
return 0;
|
|
}
|
|
|
|
ArrayList read = new ArrayList();
|
|
ArrayList write = new ArrayList();
|
|
ArrayList error = new ArrayList();
|
|
for (int i = 0; i < channelArray.get_Count(); i++)
|
|
{
|
|
SelectionKeyImpl ski = (SelectionKeyImpl)channelArray.get_Item(i);
|
|
int ops = ski.interestOps();
|
|
if (ski.channel() instanceof SocketChannelImpl)
|
|
{
|
|
// TODO there's a race condition here...
|
|
if (((SocketChannelImpl)ski.channel()).isConnected())
|
|
{
|
|
ops &= SelectionKey.OP_READ | SelectionKey.OP_WRITE;
|
|
}
|
|
else
|
|
{
|
|
ops &= SelectionKey.OP_CONNECT;
|
|
}
|
|
}
|
|
if ((ops & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0)
|
|
{
|
|
read.Add(ski.getSocket());
|
|
}
|
|
if ((ops & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0)
|
|
{
|
|
write.Add(ski.getSocket());
|
|
}
|
|
if ((ops & SelectionKey.OP_CONNECT) != 0)
|
|
{
|
|
error.Add(ski.getSocket());
|
|
}
|
|
}
|
|
read.Add(wakeupSourceFd);
|
|
try
|
|
{
|
|
begin();
|
|
int microSeconds = 1000 * (int)Math.min(Integer.MAX_VALUE / 1000, timeout);
|
|
try
|
|
{
|
|
if (false) throw new SocketException();
|
|
// FXBUG docs say that -1 is infinite timeout, but that doesn't appear to work
|
|
Socket.Select(read, write, error, timeout < 0 ? Integer.MAX_VALUE : microSeconds);
|
|
}
|
|
catch (SocketException _)
|
|
{
|
|
read.Clear();
|
|
write.Clear();
|
|
error.Clear();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
end();
|
|
}
|
|
processDeregisterQueue();
|
|
int updated = updateSelectedKeys(read, write, error);
|
|
// Done with poll(). Set wakeupSocket to nonsignaled for the next run.
|
|
resetWakeupSocket();
|
|
return updated;
|
|
}
|
|
|
|
private int updateSelectedKeys(ArrayList read, ArrayList write, ArrayList error)
|
|
{
|
|
updateCount++;
|
|
int keys = processFDSet(updateCount, read, PollArrayWrapper.POLLIN);
|
|
keys += processFDSet(updateCount, write, PollArrayWrapper.POLLCONN | PollArrayWrapper.POLLOUT);
|
|
keys += processFDSet(updateCount, error, PollArrayWrapper.POLLIN | PollArrayWrapper.POLLCONN | PollArrayWrapper.POLLOUT);
|
|
return keys;
|
|
}
|
|
|
|
private int processFDSet(long updateCount, ArrayList sockets, int rOps)
|
|
{
|
|
int numKeysUpdated = 0;
|
|
for (int i = 0; i < sockets.get_Count(); i++)
|
|
{
|
|
Socket desc = (Socket)sockets.get_Item(i);
|
|
if (desc == wakeupSourceFd)
|
|
{
|
|
synchronized (interruptLock)
|
|
{
|
|
interruptTriggered = true;
|
|
}
|
|
continue;
|
|
}
|
|
MapEntry me = fdMap.get(desc);
|
|
// If me is null, the key was deregistered in the previous
|
|
// processDeregisterQueue.
|
|
if (me == null)
|
|
continue;
|
|
SelectionKeyImpl sk = me.ski;
|
|
if (selectedKeys.contains(sk))
|
|
{ // Key in selected set
|
|
if (me.clearedCount != updateCount)
|
|
{
|
|
if (sk.channel.translateAndSetReadyOps(rOps, sk) &&
|
|
(me.updateCount != updateCount))
|
|
{
|
|
me.updateCount = updateCount;
|
|
numKeysUpdated++;
|
|
}
|
|
}
|
|
else
|
|
{ // The readyOps have been set; now add
|
|
if (sk.channel.translateAndUpdateReadyOps(rOps, sk) &&
|
|
(me.updateCount != updateCount))
|
|
{
|
|
me.updateCount = updateCount;
|
|
numKeysUpdated++;
|
|
}
|
|
}
|
|
me.clearedCount = updateCount;
|
|
}
|
|
else
|
|
{ // Key is not in selected set yet
|
|
if (me.clearedCount != updateCount)
|
|
{
|
|
sk.channel.translateAndSetReadyOps(rOps, sk);
|
|
if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0)
|
|
{
|
|
selectedKeys.add(sk);
|
|
me.updateCount = updateCount;
|
|
numKeysUpdated++;
|
|
}
|
|
}
|
|
else
|
|
{ // The readyOps have been set; now add
|
|
sk.channel.translateAndUpdateReadyOps(rOps, sk);
|
|
if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0)
|
|
{
|
|
selectedKeys.add(sk);
|
|
me.updateCount = updateCount;
|
|
numKeysUpdated++;
|
|
}
|
|
}
|
|
me.clearedCount = updateCount;
|
|
}
|
|
}
|
|
return numKeysUpdated;
|
|
}
|
|
|
|
protected void implClose() throws IOException
|
|
{
|
|
if (channelArray != null)
|
|
{
|
|
// prevent further wakeup
|
|
synchronized (interruptLock) {
|
|
interruptTriggered = true;
|
|
}
|
|
wakeupPipe.sink().close();
|
|
wakeupPipe.source().close();
|
|
for (int i = 0; i < channelArray.get_Count(); i++)
|
|
{ // Deregister channels
|
|
SelectionKeyImpl ski = (SelectionKeyImpl)channelArray.get_Item(i);
|
|
deregister(ski);
|
|
SelectableChannel selch = ski.channel();
|
|
if (!selch.isOpen() && !selch.isRegistered())
|
|
((SelChImpl)selch).kill();
|
|
}
|
|
selectedKeys = null;
|
|
channelArray = null;
|
|
}
|
|
}
|
|
|
|
protected void implRegister(SelectionKeyImpl ski)
|
|
{
|
|
channelArray.Add(ski);
|
|
fdMap.put(ski.getSocket(), new MapEntry(ski));
|
|
keys.add(ski);
|
|
}
|
|
|
|
protected void implDereg(SelectionKeyImpl ski) throws IOException
|
|
{
|
|
channelArray.Remove(ski);
|
|
fdMap.remove(ski.getSocket());
|
|
keys.remove(ski);
|
|
selectedKeys.remove(ski);
|
|
deregister(ski);
|
|
SelectableChannel selch = ski.channel();
|
|
if (!selch.isOpen() && !selch.isRegistered())
|
|
{
|
|
((SelChImpl)selch).kill();
|
|
}
|
|
}
|
|
|
|
public Selector wakeup()
|
|
{
|
|
synchronized (interruptLock)
|
|
{
|
|
if (!interruptTriggered)
|
|
{
|
|
setWakeupSocket();
|
|
interruptTriggered = true;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// Sets Windows wakeup socket to a signaled state.
|
|
private void setWakeupSocket() {
|
|
wakeupSinkFd.Send(new byte[1]);
|
|
}
|
|
|
|
// Sets Windows wakeup socket to a non-signaled state.
|
|
private void resetWakeupSocket() {
|
|
synchronized (interruptLock)
|
|
{
|
|
if (interruptTriggered == false)
|
|
return;
|
|
resetWakeupSocket0(wakeupSourceFd);
|
|
interruptTriggered = false;
|
|
}
|
|
}
|
|
|
|
private static void resetWakeupSocket0(Socket wakeupSourceFd)
|
|
{
|
|
while (wakeupSourceFd.get_Available() > 0)
|
|
{
|
|
wakeupSourceFd.Receive(new byte[1]);
|
|
}
|
|
}
|
|
}
|