com.zeroc.IceInternal.ThreadPool 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 ThreadPool implements java.util.concurrent.Executor
{
final class ShutdownWorkItem implements ThreadPoolWorkItem
{
@Override
public void execute(ThreadPoolCurrent current)
{
current.ioCompleted();
try
{
_instance.objectAdapterFactory().shutdown();
}
catch(com.zeroc.Ice.CommunicatorDestroyedException ex)
{
}
}
}
static final class FinishedWorkItem implements ThreadPoolWorkItem
{
public
FinishedWorkItem(EventHandler handler, boolean close)
{
_handler = handler;
_close = close;
}
@Override
public void execute(ThreadPoolCurrent current)
{
_handler.finished(current, _close);
}
private final EventHandler _handler;
private final boolean _close;
}
static final class JoinThreadWorkItem implements ThreadPoolWorkItem
{
public
JoinThreadWorkItem(EventHandlerThread thread)
{
_thread = thread;
}
@Override
public void execute(ThreadPoolCurrent current)
{
// No call to ioCompleted, this shouldn't block (and we don't want to cause
// a new thread to be started).
try
{
_thread.join();
}
catch (InterruptedException e)
{
// Ignore.
}
}
private final EventHandlerThread _thread;
}
static final class InterruptWorkItem implements ThreadPoolWorkItem
{
@Override
public void execute(ThreadPoolCurrent current)
{
// Nothing to do, this is just used to interrupt the thread pool selector.
}
}
//
// Exception raised by the thread pool work queue when the thread pool is destroyed.
//
static final class DestroyedException extends RuntimeException
{
public static final long serialVersionUID = 0L;
}
public
ThreadPool(Instance instance, String prefix, int timeout)
{
com.zeroc.Ice.Properties properties = instance.initializationData().properties;
_instance = instance;
_dispatcher = instance.initializationData().dispatcher;
_destroyed = false;
_prefix = prefix;
_selector = new Selector(instance);
_threadIndex = 0;
_inUse = 0;
_inUseIO = 0;
_promote = true;
_serialize = properties.getPropertyAsInt(_prefix + ".Serialize") > 0;
_serverIdleTime = timeout;
_threadPrefix = Util.createThreadName(properties, _prefix);
int nProcessors = Runtime.getRuntime().availableProcessors();
//
// We use just one thread as the default. This is the fastest
// possible setting, still allows one level of nesting, and
// doesn't require to make the servants thread safe.
//
int size = properties.getPropertyAsIntWithDefault(_prefix + ".Size", 1);
if(size < 1)
{
String s = _prefix + ".Size < 1; Size adjusted to 1";
_instance.initializationData().logger.warning(s);
size = 1;
}
int sizeMax = properties.getPropertyAsIntWithDefault(_prefix + ".SizeMax", size);
if(sizeMax == -1)
{
sizeMax = nProcessors;
}
if(sizeMax < size)
{
String s = _prefix + ".SizeMax < " + _prefix + ".Size; SizeMax adjusted to Size (" + size + ")";
_instance.initializationData().logger.warning(s);
sizeMax = size;
}
int sizeWarn = properties.getPropertyAsInt(_prefix + ".SizeWarn");
if(sizeWarn != 0 && sizeWarn < size)
{
String s = _prefix + ".SizeWarn < " + _prefix + ".Size; adjusted SizeWarn to Size (" + size + ")";
_instance.initializationData().logger.warning(s);
sizeWarn = size;
}
else if(sizeWarn > sizeMax)
{
String s = _prefix + ".SizeWarn > " + _prefix + ".SizeMax; adjusted SizeWarn to SizeMax (" + sizeMax + ")";
_instance.initializationData().logger.warning(s);
sizeWarn = sizeMax;
}
int threadIdleTime = properties.getPropertyAsIntWithDefault(_prefix + ".ThreadIdleTime", 60);
if(threadIdleTime < 0)
{
String s = _prefix + ".ThreadIdleTime < 0; ThreadIdleTime adjusted to 0";
_instance.initializationData().logger.warning(s);
threadIdleTime = 0;
}
_size = size;
_sizeMax = sizeMax;
_sizeWarn = sizeWarn;
_sizeIO = Math.min(sizeMax, nProcessors);
_threadIdleTime = threadIdleTime;
int stackSize = properties.getPropertyAsInt( _prefix + ".StackSize");
if(stackSize < 0)
{
String s = _prefix + ".StackSize < 0; Size adjusted to JRE default";
_instance.initializationData().logger.warning(s);
stackSize = 0;
}
_stackSize = stackSize;
boolean hasPriority = properties.getProperty(_prefix + ".ThreadPriority").length() > 0;
int priority = properties.getPropertyAsInt(_prefix + ".ThreadPriority");
if(!hasPriority)
{
hasPriority = properties.getProperty("Ice.ThreadPriority").length() > 0;
priority = properties.getPropertyAsInt("Ice.ThreadPriority");
}
_hasPriority = hasPriority;
_priority = priority;
_workQueue = new ThreadPoolWorkQueue(_instance, this, _selector);
_nextHandler = _handlers.iterator();
if(_instance.traceLevels().threadPool >= 1)
{
String s = "creating " + _prefix + ": Size = " + _size + ", SizeMax = " + _sizeMax + ", SizeWarn = " +
_sizeWarn;
_instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
}
try
{
for(int i = 0; i < _size; i++)
{
EventHandlerThread thread = new EventHandlerThread(_threadPrefix + "-" + _threadIndex++);
if(_hasPriority)
{
thread.start(_priority);
}
else
{
thread.start(java.lang.Thread.NORM_PRIORITY);
}
_threads.add(thread);
}
}
catch(RuntimeException ex)
{
String s = "cannot create thread for `" + _prefix + "':\n" + Ex.toString(ex);
_instance.initializationData().logger.error(s);
destroy();
try
{
joinWithAllThreads();
}
catch (InterruptedException e)
{
throw new com.zeroc.Ice.OperationInterruptedException();
}
throw ex;
}
}
@SuppressWarnings("deprecation")
@Override
protected synchronized void
finalize()
throws Throwable
{
try
{
com.zeroc.IceUtilInternal.Assert.FinalizerAssert(_destroyed);
}
catch(java.lang.Exception ex)
{
}
finally
{
super.finalize();
}
}
public synchronized void
destroy()
{
if(_destroyed)
{
return;
}
_destroyed = true;
_workQueue.destroy();
}
public synchronized void
updateObservers()
{
for(EventHandlerThread thread : _threads)
{
thread.updateObserver();
}
}
public synchronized void
initialize(final EventHandler handler)
{
assert(!_destroyed);
_selector.initialize(handler);
handler.setReadyCallback(
new ReadyCallback()
{
public void ready(int op, boolean value)
{
synchronized(ThreadPool.this)
{
if(_destroyed)
{
return;
}
_selector.ready(handler, op, value);
}
}
});
}
public void
register(EventHandler handler, int op)
{
update(handler, SocketOperation.None, op);
}
public synchronized void
update(EventHandler handler, int remove, int add)
{
assert(!_destroyed);
// Don't remove what needs to be added
remove &= ~add;
// Don't remove/add if already un-registered or registered
remove = handler._registered & remove;
add = ~handler._registered & add;
if(remove == add)
{
return;
}
_selector.update(handler, remove, add);
}
public void
unregister(EventHandler handler, int op)
{
update(handler, op, SocketOperation.None);
}
public synchronized boolean
finish(EventHandler handler, boolean closeNow)
{
assert(!_destroyed);
closeNow = _selector.finish(handler, closeNow);
_workQueue.queue(new FinishedWorkItem(handler, !closeNow));
return closeNow;
}
public void
dispatchFromThisThread(DispatchWorkItem workItem)
{
if(_dispatcher != null)
{
try
{
_dispatcher.accept(workItem, workItem.getConnection());
}
catch(java.lang.Exception ex)
{
if(_instance.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 1)
{
java.io.StringWriter sw = new java.io.StringWriter();
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
ex.printStackTrace(pw);
pw.flush();
_instance.initializationData().logger.warning("dispatch exception:\n" + sw.toString());
}
}
}
else
{
workItem.run();
}
}
synchronized public void
dispatch(DispatchWorkItem workItem)
{
if(_destroyed)
{
throw new com.zeroc.Ice.CommunicatorDestroyedException();
}
_workQueue.queue(workItem);
}
public void
joinWithAllThreads()
throws InterruptedException
{
//
// _threads is immutable after destroy() has been called,
// therefore no synchronization is needed. (Synchronization
// wouldn't be possible here anyway, because otherwise the
// other threads would never terminate.)
//
for(EventHandlerThread thread : _threads)
{
thread.join();
}
//
// Destroy the selector
//
_selector.destroy();
}
//
// Implement execute method from java.util.concurrent.Executor interface
//
@Override
public void execute(Runnable command)
{
dispatch(new com.zeroc.IceInternal.DispatchWorkItem()
{
@Override
public void run()
{
command.run();
}
});
}
private void
run(EventHandlerThread thread)
{
ThreadPoolCurrent current = new ThreadPoolCurrent(_instance, this, thread);
boolean select = false;
while(true)
{
if(current._handler != null)
{
try
{
current._handler.message(current);
}
catch(DestroyedException ex)
{
synchronized(this)
{
--_inUse;
thread.setState(com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateIdle);
}
return;
}
catch(java.lang.Exception ex)
{
String s = "exception in `" + _prefix + "':\n" + Ex.toString(ex);
s += "\nevent handler: " + current._handler.toString();
_instance.initializationData().logger.error(s);
}
}
else if(select)
{
try
{
_selector.select(_serverIdleTime);
}
catch(Selector.TimeoutException ex)
{
synchronized(this)
{
if(!_destroyed && _inUse == 0)
{
_workQueue.queue(new ShutdownWorkItem()); // Select timed-out.
}
continue;
}
}
}
synchronized(this)
{
if(current._handler == null)
{
if(select)
{
_selector.finishSelect(_handlers);
select = false;
_nextHandler = _handlers.iterator();
}
else if(!current._leader && followerWait(current))
{
return; // Wait timed-out.
}
}
else if(_sizeMax > 1)
{
if(!current._ioCompleted)
{
//
// The handler didn't call ioCompleted() so we take care of decreasing
// the IO thread count now.
//
--_inUseIO;
}
else
{
//
// If the handler called ioCompleted(), we re-enable the handler in
// case it was disabled and we decrease the number of thread in use.
//
if(_serialize)
{
_selector.enable(current._handler, current.operation);
}
assert(_inUse > 0);
--_inUse;
}
if(!current._leader && followerWait(current))
{
return; // Wait timed-out.
}
}
//
// Get the next ready handler.
//
current._handler = null;
while(_nextHandler.hasNext())
{
EventHandlerOpPair n = _nextHandler.next();
int op = n.op & ~n.handler._disabled & n.handler._registered;
if(op != 0)
{
current._ioCompleted = false;
current._handler = n.handler;
current.operation = op;
thread.setState(com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateInUseForIO);
break;
}
}
if(current._handler == null)
{
//
// If there are no more ready handlers and there are still threads busy performing
// IO, we give up leadership and promote another follower (which will perform the
// select() only once all the IOs are completed). Otherwise, if there's no more
// threads peforming IOs, it's time to do another select().
//
if(_inUseIO > 0)
{
promoteFollower(current);
}
else
{
_handlers.clear();
_selector.startSelect();
select = true;
thread.setState(com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateIdle);
}
}
else if(_sizeMax > 1)
{
//
// Increment the IO thread count and if there's still threads available
// to perform IO and more handlers ready, we promote a follower.
//
++_inUseIO;
if(_nextHandler.hasNext() && _inUseIO < _sizeIO)
{
promoteFollower(current);
}
}
}
}
}
synchronized void
ioCompleted(ThreadPoolCurrent current)
{
current._ioCompleted = true; // Set the IO completed flag to specify that ioCompleted() has been called.
current._thread.setState(com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateInUseForUser);
if(_sizeMax > 1)
{
--_inUseIO;
if(!_destroyed)
{
if(_serialize)
{
_selector.disable(current._handler, current.operation);
}
}
if(current._leader)
{
//
// If this thread is still the leader, it's time to promote a new leader.
//
promoteFollower(current);
}
else if(_promote && (_nextHandler.hasNext() || _inUseIO == 0))
{
notify();
}
assert(_inUse >= 0);
++_inUse;
if(_inUse == _sizeWarn)
{
String s = "thread pool `" + _prefix + "' is running low on threads\n"
+ "Size=" + _size + ", " + "SizeMax=" + _sizeMax + ", " + "SizeWarn=" + _sizeWarn;
_instance.initializationData().logger.warning(s);
}
if(!_destroyed)
{
assert(_inUse <= _threads.size());
if(_inUse < _sizeMax && _inUse == _threads.size())
{
if(_instance.traceLevels().threadPool >= 1)
{
String s = "growing " + _prefix + ": Size=" + (_threads.size() + 1);
_instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
}
try
{
EventHandlerThread thread = new EventHandlerThread(_threadPrefix + "-" + _threadIndex++);
_threads.add(thread);
if(_hasPriority)
{
thread.start(_priority);
}
else
{
thread.start(java.lang.Thread.NORM_PRIORITY);
}
}
catch(RuntimeException ex)
{
String s = "cannot create thread for `" + _prefix + "':\n" + Ex.toString(ex);
_instance.initializationData().logger.error(s);
}
}
}
}
}
private synchronized void
promoteFollower(ThreadPoolCurrent current)
{
assert(!_promote && current._leader);
_promote = true;
if(_inUseIO < _sizeIO && (_nextHandler.hasNext() || _inUseIO == 0))
{
notify();
}
current._leader = false;
}
private synchronized boolean
followerWait(ThreadPoolCurrent current)
{
assert(!current._leader);
current._thread.setState(com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateIdle);
//
// It's important to clear the handler before waiting to make sure that
// resources for the handler are released now if it's finished. We also
// clear the per-thread stream.
//
current._handler = null;
current.stream.reset();
//
// Wait to be promoted and for all the IO threads to be done.
//
while(!_promote || _inUseIO == _sizeIO || (!_nextHandler.hasNext() && _inUseIO > 0))
{
if(_threadIdleTime > 0)
{
long before = Time.currentMonotonicTimeMillis();
boolean interrupted = false;
try
{
//
// If the wait is interrupted then we'll let the thread die as if it timed out.
//
wait(_threadIdleTime * 1000);
}
catch (InterruptedException e)
{
interrupted = true;
}
if(interrupted || Time.currentMonotonicTimeMillis() - before >= _threadIdleTime * 1000)
{
if(!_destroyed && (!_promote || _inUseIO == _sizeIO || (!_nextHandler.hasNext() && _inUseIO > 0)))
{
if(_instance.traceLevels().threadPool >= 1)
{
String s = "shrinking " + _prefix + ": Size=" + (_threads.size() - 1);
_instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s);
}
assert(_threads.size() > 1); // Can only be called by a waiting follower thread.
_threads.remove(current._thread);
_workQueue.queue(new JoinThreadWorkItem(current._thread));
return true;
}
}
}
else
{
try
{
wait();
}
catch (InterruptedException e)
{
//
// Eat the InterruptedException.
//
}
}
}
current._leader = true; // The current thread has become the leader.
_promote = false;
return false;
}
private final Instance _instance;
private final java.util.function.BiConsumer _dispatcher;
private final ThreadPoolWorkQueue _workQueue;
private boolean _destroyed;
private final String _prefix;
private final String _threadPrefix;
private final Selector _selector;
final class EventHandlerThread implements Runnable
{
EventHandlerThread(String name)
{
_name = name;
_state = com.zeroc.Ice.Instrumentation.ThreadState.ThreadStateIdle;
updateObserver();
}
public void
updateObserver()
{
// Must be called with the thread pool mutex locked
com.zeroc.Ice.Instrumentation.CommunicatorObserver obsv = _instance.initializationData().observer;
if(obsv != null)
{
_observer = obsv.getThreadObserver(_prefix, _name, _state, _observer);
if(_observer != null)
{
_observer.attach();
}
}
}
public void
setState(com.zeroc.Ice.Instrumentation.ThreadState s)
{
// Must be called with the thread pool mutex locked
if(_observer != null)
{
if(_state != s)
{
_observer.stateChanged(_state, s);
}
}
_state = s;
}
public void
join()
throws InterruptedException
{
_thread.join();
}
public void
start(int priority)
{
_thread = new Thread(null, this, _name, _stackSize);
_thread.setPriority(priority);
_thread.start();
}
@Override
public void
run()
{
if(_instance.initializationData().threadStart != null)
{
try
{
_instance.initializationData().threadStart.run();
}
catch(java.lang.Exception ex)
{
String s = "threadStart method raised an unexpected exception in `";
s += _prefix + "' thread " + _name + ":\n" + Ex.toString(ex);
_instance.initializationData().logger.error(s);
}
}
try
{
ThreadPool.this.run(this);
}
catch(java.lang.Exception ex)
{
String s = "exception in `" + _prefix + "' thread " + _name + ":\n" + Ex.toString(ex);
_instance.initializationData().logger.error(s);
}
if(_observer != null)
{
_observer.detach();
}
if(_instance.initializationData().threadStop != null)
{
try
{
_instance.initializationData().threadStop.run();
}
catch(java.lang.Exception ex)
{
String s = "threadStop method raised an unexpected exception in `";
s += _prefix + "' thread " + _name + ":\n" + Ex.toString(ex);
_instance.initializationData().logger.error(s);
}
}
}
Thread getThread()
{
return _thread;
}
final private String _name;
private Thread _thread;
private com.zeroc.Ice.Instrumentation.ThreadState _state;
private com.zeroc.Ice.Instrumentation.ThreadObserver _observer;
}
private final int _size; // Number of threads that are pre-created.
private final int _sizeIO; // Number of threads that can concurrently perform IO.
private final int _sizeMax; // Maximum number of threads.
private final int _sizeWarn; // If _inUse reaches _sizeWarn, a "low on threads" warning will be printed.
private final boolean _serialize; // True if requests need to be serialized over the connection.
private final int _priority;
private final boolean _hasPriority;
private final long _serverIdleTime;
private final long _threadIdleTime;
private final int _stackSize;
private java.util.List _threads = new java.util.ArrayList<>();
private int _threadIndex; // For assigning thread names.
private int _inUse; // Number of threads that are currently in use.
private int _inUseIO; // Number of threads that are currently performing IO.
private java.util.List _handlers = new java.util.ArrayList<>();
private java.util.Iterator _nextHandler;
private boolean _promote;
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy