com.zeroc.IceInternal.Selector Maven / Gradle / Ivy
//
// 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;
}