/*******************************************************************************
* Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-3.0-standalone.html
******************************************************************************/
package se.unlogic.standardutils.threads;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadPoolTaskGroupHandler<T extends TaskGroup> implements TaskGroupHandler<T,SimpleExecutionController<T>>, Runnable{
protected final ReentrantLock taskGroupRemoveLock = new ReentrantLock();
protected final Condition taskGroupRemoveCondition = taskGroupRemoveLock.newCondition();
protected final ReentrantLock taskGroupAddLock = new ReentrantLock();
protected final Condition taskGroupAddCondition = taskGroupAddLock.newCondition();
protected final CopyOnWriteArrayList<SimpleExecutionController<T>> taskGroupList = new CopyOnWriteArrayList<SimpleExecutionController<T>>();
protected int taskGroupIndex = 0;
protected Status status = Status.RUNNING;
protected final ArrayList<Thread> threads = new ArrayList<Thread>();
public ThreadPoolTaskGroupHandler(int poolSize, boolean daemon){
while(threads.size() < poolSize){
Thread thread = new Thread(this, "TaskGroupHandler thread " + (threads.size() + 1));
thread.setDaemon(daemon);
threads.add(thread);
thread.start();
}
}
public void run() {
Runnable task = null;
while(true){
while(!taskGroupList.isEmpty() && status != Status.SHUTDOWN){
SimpleExecutionController<T> executionController;
try {
executionController = this.taskGroupList.get(this.getIndex());
} catch (IndexOutOfBoundsException e) {
continue;
}
task = executionController.getTaskQueue().poll();
if(task == null){
taskGroupRemoveLock.lock();
try{
if(executionController.getInitialTaskCount() == executionController.getCompletedTaskCount() && this.taskGroupList.contains(executionController)){
this.taskGroupList.remove(executionController);
this.taskGroupRemoveCondition.signalAll();
executionController.executionComplete();
}else{
break;
}
}finally{
taskGroupRemoveLock.unlock();
}
}else{
try{
task.run();
}catch(Throwable e){
e.printStackTrace();
}finally{
executionController.incrementCompletedTaskCount();
}
}
}
taskGroupAddLock.lock();
try{
if(status == Status.TERMINATING){
//Last thread sets shutdown status
if(getLiveThreads() == 1){
status = Status.SHUTDOWN;
}
return;
}
if(this.isEmpty()){
taskGroupAddCondition.await();
}
} catch (InterruptedException e) {
}finally{
taskGroupAddLock.unlock();
}
}
}
public boolean isEmpty() {
if(this.taskGroupList.isEmpty()){
return true;
}
for(SimpleExecutionController<T> controller : this.taskGroupList){
if(!controller.getTaskQueue().isEmpty()){
return false;
}
}
return true;
}
public int getLiveThreads(){
int activeThreadCount = 0;
for(Thread thread : threads){
if(thread.isAlive()){
activeThreadCount++;
}
}
return activeThreadCount;
}
protected synchronized int getIndex() {
taskGroupIndex++;
if(taskGroupIndex >= taskGroupList.size()){
taskGroupIndex = 0;
}
return taskGroupIndex;
}
void remove(SimpleExecutionController<T> executionController){
taskGroupRemoveLock.lock();
try{
this.taskGroupList.remove(executionController);
this.taskGroupRemoveCondition.signalAll();
}finally{
taskGroupRemoveLock.unlock();
}
}
void add(SimpleExecutionController<T> executionController){
taskGroupAddLock.lock();
try{
if(status == Status.RUNNING){
this.taskGroupList.add(executionController);
taskGroupAddCondition.signalAll();
}else{
throw new RejectedExecutionException("TaskGroupHandler status " + status);
}
}finally{
taskGroupAddLock.unlock();
}
}
public void abortAllTaskGroups() {
taskGroupRemoveLock.lock();
try{
while(!this.taskGroupList.isEmpty()){
for(SimpleExecutionController<T> executionController : taskGroupList){
executionController.abort();
}
}
}finally{
taskGroupRemoveLock.unlock();
}
}
public SimpleExecutionController<T> execute(T taskGroup) throws RejectedExecutionException{
if(status == Status.RUNNING){
return new SimpleExecutionController<T>(taskGroup, this);
}else{
throw new RejectedExecutionException("TaskGroupHandler status " + status);
}
}
public int getTaskGroupCount() {
return this.taskGroupList.size();
}
public List<SimpleExecutionController<T>> getTaskGroups() {
return new ArrayList<SimpleExecutionController<T>>(this.taskGroupList);
}
public int getTotalTaskCount() {
int taskCount = 0;
for(SimpleExecutionController<T> executionController : taskGroupList){
taskCount += executionController.getTaskQueue().size();
}
return taskCount;
}
public Status getStatus(){
return this.status;
}
public void awaitTermination() throws InterruptedException {
taskGroupRemoveLock.lock();
try{
while(!this.taskGroupList.isEmpty()){
taskGroupRemoveCondition.await();
}
}finally{
taskGroupRemoveLock.unlock();
}
}
public void awaitTermination(long timeout) throws InterruptedException {
taskGroupRemoveLock.lock();
try{
while(!this.taskGroupList.isEmpty()){
taskGroupRemoveCondition.await(timeout,TimeUnit.MILLISECONDS);
}
}finally{
taskGroupRemoveLock.unlock();
}
}
public void shutdown() {
taskGroupAddLock.lock();
try{
if(this.status == Status.RUNNING){
this.status = Status.TERMINATING;
this.taskGroupAddCondition.signalAll();
}
}finally{
taskGroupAddLock.unlock();
}
}
public void shutdownNow() {
this.shutdown();
this.abortAllTaskGroups();
}
}