com.zeroc.IceInternal.Selector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ice Show documentation
Show all versions of ice Show documentation
Ice is a comprehensive RPC framework that helps you build distributed applications with minimal effort using familiar object-oriented idioms
//
// Copyright (c) ZeroC, Inc. All rights reserved.
//
package com.zeroc.IceInternal;
public final class Selector
{
static final class TimeoutException extends Exception
{
public static final long serialVersionUID = 0L;
}
Selector(Instance instance)
{
_instance = instance;
_selecting = false;
_interrupted = false;
try
{
_selector = java.nio.channels.Selector.open();
}
catch(java.io.IOException ex)
{
throw new com.zeroc.Ice.SyscallException(ex);
}
//
// The Selector holds a Set representing the selected keys. The
// Set reference doesn't change, so we obtain it once here.
//
_keys = _selector.selectedKeys();
}
void destroy()
{
try
{
_selector.close();
}
catch(java.io.IOException ex)
{
}
_selector = null;
}
void initialize(EventHandler handler)
{
if(handler.fd() != null)
{
updateImpl(handler);
}
}
void update(EventHandler handler, int remove, int add)
{
int previous = handler._registered;
handler._registered = handler._registered & ~remove;
handler._registered = handler._registered | add;
if(previous == handler._registered)
{
return;
}
if(handler.fd() != null)
{
updateImpl(handler);
}
checkReady(handler);
}
void enable(EventHandler handler, int status)
{
if((handler._disabled & status) == 0)
{
return;
}
handler._disabled = handler._disabled & ~status;
if(handler._key != null && (handler._registered & status) != 0)
{
updateImpl(handler); // If registered with the selector, update the registration.
}
checkReady(handler);
}
void disable(EventHandler handler, int status)
{
if((handler._disabled & status) != 0)
{
return;
}
handler._disabled = handler._disabled | status;
if(handler._key != null && (handler._registered & status) != 0)
{
updateImpl(handler); // If registered with the selector, update the registration.
}
checkReady(handler);
}
boolean finish(EventHandler handler, boolean closeNow)
{
handler._registered = 0;
if(handler._key != null)
{
handler._key.cancel();
handler._key = null;
}
_changes.remove(handler);
checkReady(handler);
return closeNow;
}
void ready(EventHandler handler, int status, boolean value)
{
if(((handler._ready & status) != 0) == value)
{
return; // Nothing to do if ready state already correctly set.
}
if(value)
{
handler._ready |= status;
}
else
{
handler._ready &= ~status;
}
checkReady(handler);
}
void startSelect()
{
if(!_changes.isEmpty())
{
updateSelector();
}
_selecting = true;
//
// If there are ready handlers, don't block in select, just do a non-blocking
// select to retrieve new ready handlers from the Java selector.
//
_selectNow = !_readyHandlers.isEmpty();
}
void finishSelect(java.util.List handlers)
{
assert(handlers.isEmpty());
if(_keys.isEmpty() && _readyHandlers.isEmpty() && !_interrupted) // If key set is empty and we weren't woken up.
{
//
// This is necessary to prevent a busy loop in case of a spurious wake-up which
// sometime occurs in the client thread pool when the communicator is destroyed.
// If there are too many successive spurious wake-ups, we log an error.
//
try
{
Thread.sleep(1);
}
catch(InterruptedException ex)
{
//
// Eat the InterruptedException (as we do in ThreadPool.promoteFollower).
//
}
if(++_spuriousWakeUp > 100)
{
_spuriousWakeUp = 0;
_instance.initializationData().logger.warning("spurious selector wake up");
}
return;
}
_interrupted = false;
_spuriousWakeUp = 0;
for(java.nio.channels.SelectionKey key : _keys)
{
EventHandler handler = (EventHandler)key.attachment();
try
{
//
// Use the intersection of readyOps and interestOps because we only want to
// report the operations in which the handler is still interested.
//
final int op = fromJavaOps(key.readyOps() & key.interestOps());
if(!_readyHandlers.contains(handler)) // Handler will be added by the loop below
{
handlers.add(new EventHandlerOpPair(handler, op));
}
}
catch(java.nio.channels.CancelledKeyException ex)
{
assert(handler._registered == 0);
}
}
for(EventHandler handler : _readyHandlers)
{
int op = handler._ready & ~handler._disabled & handler._registered;
if(handler._key != null && _keys.contains(handler._key))
{
op |= fromJavaOps(handler._key.readyOps() & handler._key.interestOps());
}
if(op > 0)
{
handlers.add(new EventHandlerOpPair(handler, op));
}
}
_keys.clear();
_selecting = false;
}
void select(long timeout)
throws TimeoutException
{
while(true)
{
try
{
if(_selectNow)
{
_selector.selectNow();
}
else if(timeout > 0)
{
//
// NOTE: On some platforms, select() sometime returns slightly before
// the timeout (at least according to our monotonic time). To make sure
// timeouts are correctly detected, we wait for a little longer than
// the configured timeout (10ms).
//
long before = Time.currentMonotonicTimeMillis();
if(_selector.select(timeout * 1000 + 10) == 0)
{
if(Time.currentMonotonicTimeMillis() - before >= timeout * 1000)
{
throw new TimeoutException();
}
}
}
else
{
_selector.select();
}
}
catch(java.nio.channels.CancelledKeyException ex)
{
// This sometime occurs on macOS, ignore.
continue;
}
catch(java.io.IOException ex)
{
//
// Pressing Ctrl-C causes select() to raise an
// IOException, which seems like a JDK bug. We trap
// for that special case here and ignore it.
// Hopefully we're not masking something important!
//
if(Network.interrupted(ex))
{
continue;
}
try
{
String s = "selector failed:\n" + ex.getCause().getMessage();
try
{
_instance.initializationData().logger.error(s);
}
catch(Throwable ex1)
{
System.out.println(s);
}
}
catch(Throwable ex2)
{
// Ignore
}
try
{
Thread.sleep(1);
}
catch(InterruptedException ex2)
{
}
}
break;
}
}
private void updateImpl(EventHandler handler)
{
_changes.add(handler);
wakeup();
}
private void updateSelector()
{
for(EventHandler handler : _changes)
{
int status = handler._registered & ~handler._disabled;
int ops = toJavaOps(handler, status);
if(handler._key == null)
{
if(handler._registered != 0)
{
try
{
handler._key = handler.fd().register(_selector, ops, handler);
}
catch(java.nio.channels.ClosedChannelException ex)
{
assert(false);
}
}
}
else
{
handler._key.interestOps(ops);
}
}
_changes.clear();
}
private void checkReady(EventHandler handler)
{
if((handler._ready & ~handler._disabled & handler._registered) != 0)
{
_readyHandlers.add(handler);
if(_selecting)
{
wakeup();
}
}
else
{
_readyHandlers.remove(handler);
}
}
private void wakeup()
{
if(_selecting && !_interrupted)
{
_selector.wakeup();
_interrupted = true;
}
}
private int toJavaOps(EventHandler handler, int o)
{
int op = 0;
if((o & SocketOperation.Read) != 0)
{
if((handler.fd().validOps() & java.nio.channels.SelectionKey.OP_READ) != 0)
{
op |= java.nio.channels.SelectionKey.OP_READ;
}
else
{
op |= java.nio.channels.SelectionKey.OP_ACCEPT;
}
}
if((o & SocketOperation.Write) != 0)
{
op |= java.nio.channels.SelectionKey.OP_WRITE;
}
if((o & SocketOperation.Connect) != 0)
{
op |= java.nio.channels.SelectionKey.OP_CONNECT;
}
return op;
}
private int fromJavaOps(int o)
{
int op = 0;
if((o & (java.nio.channels.SelectionKey.OP_READ | java.nio.channels.SelectionKey.OP_ACCEPT)) != 0)
{
op |= SocketOperation.Read;
}
if((o & java.nio.channels.SelectionKey.OP_WRITE) != 0)
{
op |= SocketOperation.Write;
}
if((o & java.nio.channels.SelectionKey.OP_CONNECT) != 0)
{
op |= SocketOperation.Connect;
}
return op;
}
final private Instance _instance;
private java.nio.channels.Selector _selector;
private java.util.Set _keys;
private java.util.HashSet _changes = new java.util.HashSet<>();
private java.util.HashSet _readyHandlers = new java.util.HashSet<>();
private boolean _selecting;
private boolean _selectNow;
private boolean _interrupted;
private int _spuriousWakeUp;
}