/*
* Copyright (c), WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.carbon.humantask.core.scheduler;
import org.wso2.carbon.humantask.core.utils.CollectionsX;
import org.wso2.carbon.humantask.core.utils.MemberOfFunction;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Implements the "todo" queue and prioritized scheduling mechanism.
*/
class SchedulerThread implements Runnable {
private static final int TODO_QUEUE_INITIAL_CAPACITY = 200;
/**
* Jobs ready for immediate execution.
*/
private PriorityBlockingQueue<Task> todo;
/**
* Lock for managing the queue
*/
private ReentrantLock lock = new ReentrantLock();
private Condition activity = lock.newCondition();
private volatile boolean done;
private TaskRunner taskrunner;
private Thread thread;
SchedulerThread(TaskRunner runner) {
todo = new PriorityBlockingQueue<Task>(TODO_QUEUE_INITIAL_CAPACITY,
new JobComparatorByDate());
taskrunner = runner;
}
void start() {
if (thread != null) {
return;
}
done = false;
thread = new Thread(this, "HTScheduler");
thread.start();
}
/**
* Shutdown the thread.
*/
void stop() {
if (thread == null) {
return;
}
done = true;
lock.lock();
try {
activity.signal();
} finally {
lock.unlock();
}
while (thread != null) {
try {
thread.join();
thread = null;
} catch (InterruptedException ignored) {
}
}
}
/**
* Add a job to the todo queue.
*
* @param task Task/Job
*/
void enqueue(Task task) {
lock.lock();
try {
todo.add(task);
activity.signal();
} finally {
lock.unlock();
}
}
/**
* Remove a job from the todo queue.
*
* @param task Task/Job
*/
void dequeue(Task task) {
lock.lock();
try {
todo.remove(task);
activity.signal();
} finally {
lock.unlock();
}
}
/**
* Get the size of the todo queue.
*
* @return Size of the todo queue
*/
public int size() {
return todo.size();
}
/**
* Pop items off the todo queue, and send them to the task runner for processing.
*/
public void run() {
while (!done) {
lock.lock();
try {
long nextjob = nextJobTime();
while ((nextjob) > 0 && !done) {
activity.await(nextjob, TimeUnit.MILLISECONDS);
nextjob = nextJobTime();
}
if (!done && nextjob == 0) {
Task task = todo.take();
taskrunner.runTask(task);
}
} catch (InterruptedException ignore) {
} finally {
lock.unlock();
}
}
}
/**
* Calculate the time until the next available job.
*
* @return time until next job, 0 if one is one is scheduled to go, and some
* really large number if there are no jobs to speak of
*/
private long nextJobTime() {
assert lock.isLocked();
Task job = todo.peek();
if (job == null) {
return Long.MAX_VALUE;
}
return Math.max(0, job.schedDate - System.currentTimeMillis());
}
/**
* Remove the tasks of a given type from the list.
*
* @param tasktype type of task
*/
public void clearTasks(final Class<? extends Task> tasktype) {
lock.lock();
try {
CollectionsX.removeIf(todo, new MemberOfFunction<Task>() {
@Override
public boolean isMember(Task o) {
return tasktype.isAssignableFrom(o.getClass());
}
});
} finally {
lock.unlock();
}
}
}