package org.jacorb.util.threadpool;
/*
* JacORB - a free Java ORB
*
* Copyright (C) 1999-2014 Gerald Brose / The JacORB Team.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
import java.util.LinkedList;
import org.slf4j.Logger;
import org.jacorb.config.Configuration;
import org.omg.CORBA.NO_RESOURCES;
/**
* @author Nicolas Noffke
*/
public class ThreadPool
{
private final int max_threads;
private final int max_idle_threads;
private int total_threads = 0;
private int idle_threads = 0;
private final LinkedList job_queue;
private final ConsumerFactory factory;
private final String namePrefix;
private int threadCount = 0;
/**
* <code>logger</code> is the logger for threadpool.
*/
private final Logger logger;
/**
* <code>shutdown</code> denotes whether to shutdown the pool.
*/
private boolean shutdown;
public ThreadPool( Configuration configuration,
String threadNamePrefix,
ConsumerFactory factory,
int max_threads,
int max_idle_threads)
{
namePrefix = threadNamePrefix;
this.job_queue = new LinkedList ();
this.factory = factory;
this.max_threads = max_threads;
this.max_idle_threads = max_idle_threads;
logger = configuration.getLogger("org.jacorb.util.tpool");
}
protected synchronized Object getJob()
{
idle_threads++;
/*
* Check job queue is empty before having surplus idle threads exit,
* otherwise (as was done previously) if a large number of jobs get
* enqueued just after a large number of previously non-idle threads
* complete their jobs, then all the newly created threads as well as
* the newly non-idle threads will exit until max_idle_threads is
* reached before serving up any jobs, and if there are more jobs than
* max_idle_threads despite the fact that enough threads once existed to
* handle the queued jobs, the excess jobs will be blocked until the
* jobs taking up the max_idle_threads complete.
*
* Also, checking the idle_thread count every time the thread is
* notified and the job queue is empty ensures that the surplus idle
* threads get cleaned up more quickly, otherwise idle threads can only
* be cleaned up after completing a job.
*/
while( (! shutdown) && job_queue.isEmpty() )
{
/*
* This tells the newly idle thread to exit, because
* there are already too much idle threads.
*/
if (idle_threads > max_idle_threads)
{
if (logger.isDebugEnabled())
{
logger.debug("[" + idle_threads + "/" + total_threads +
"] Telling thread to exit (too many idle)");
}
return getShutdownJob();
}
try
{
if (logger.isDebugEnabled())
{
logger.debug("[" + idle_threads + "/" + total_threads +
"] job queue empty");
}
wait();
}
catch( InterruptedException e )
{
// ignored
}
}
//pool is to be shut down completely
if (shutdown)
{
return getShutdownJob();
}
idle_threads--;
if (logger.isDebugEnabled())
{
logger.debug("[" + idle_threads + "/" + total_threads +
"] removed idle thread (job scheduled)");
}
return job_queue.removeFirst();
}
/**
* the returned null will cause the ConsumerTie to exit.
* also decrement thread counters.
*/
private Object getShutdownJob()
{
total_threads--;
idle_threads--;
return null;
}
public synchronized void putJob( Object job )
{
job_queue.add(job);
notifyAll();
/*
* Create a new thread if there aren't enough idle threads
* to handle all the jobs in the queue and we haven't reached
* the max thread limit. This ensures that there are always
* enough idle threads to handle all the jobs in the queue
* and no jobs get stuck blocked waiting for a thread to become
* idle while we are still below the max thread limit.
*/
if ((job_queue.size() > idle_threads) &&
(total_threads < max_threads))
{
createNewThread();
}
else if ( job_queue.size() > idle_threads )
{
// more jobs than idle threads. however
// thread limit reached.
if (logger.isDebugEnabled())
{
logger.debug
(
"(Pool)[" + idle_threads + "/" + total_threads +
"] no idle threads but maximum number of threads reached (" +
max_threads + ")"
);
}
job_queue.remove(job);
throw new NO_RESOURCES("(Pool)[" + idle_threads + "/" + total_threads +
"] no idle threads but maximum number of threads reached (" +
max_threads + ")");
}
}
private void createNewThread()
{
if (logger.isDebugEnabled())
{
logger.debug("[" + idle_threads + "/" + total_threads +
"] creating new thread" );
}
Thread thread = new Thread( new ConsumerTie( this, factory.create() ));
thread.setName(namePrefix + (threadCount++));
thread.setDaemon( true );
thread.start();
total_threads++;
}
/**
* <code>getLogger</code> returns the threadpools logger.
*
* @return a <code>Logger</code> value
*/
Logger getLogger ()
{
return logger;
}
/**
* <code>shutdown</code> will shutdown the pool.
*/
public synchronized void shutdown()
{
if (logger.isDebugEnabled())
{
logger.debug("[" + idle_threads + "/" + total_threads +
"] shutting down pool" );
}
shutdown = true;
notifyAll();
}
} // ThreadPool