blob: a7cdc0e0bcac98ae5027ad730d316325d1076f7e [file] [log] [blame]
/**
* Copyright 2011, Big Switch Networks, Inc.
* Originally created by David Erickson, Stanford University
*
* 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 net.floodlightcontroller.core.util;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This allows you to represent a task that should be queued for future execution
* but where you only want the task to complete once in response to some sequence
* of events. For example, if you get a change notification and want to reload state,
* you only want to reload the state once, at the end, and don't want to queue
* an update for every notification that might come in.
* <p/>
* The semantics are as follows:
* * If the task hasn't begun yet, do not queue a new task
* * If the task has begun, set a bit to restart it after the current task finishes
*/
public class SingletonTask {
protected final static Logger logger = LoggerFactory.getLogger(SingletonTask.class);
protected static class SingletonTaskContext {
protected boolean taskShouldRun = false;
protected boolean taskRunning = false;
protected SingletonTaskWorker waitingTask = null;
}
protected static class SingletonTaskWorker implements Runnable {
SingletonTask parent;
boolean canceled = false;
long nextschedule = 0;
public SingletonTaskWorker(SingletonTask parent) {
super();
this.parent = parent;
}
@Override
@LogMessageDoc(level = "ERROR",
message = "Exception while executing task",
recommendation = LogMessageDoc.GENERIC_ACTION)
public void run() {
synchronized (parent.context) {
if (canceled || !parent.context.taskShouldRun)
return;
parent.context.taskRunning = true;
parent.context.taskShouldRun = false;
}
try {
parent.task.run();
} catch (Exception e) {
logger.error("Exception while executing task", e);
}
synchronized (parent.context) {
parent.context.taskRunning = false;
if (parent.context.taskShouldRun) {
long now = System.nanoTime();
if ((nextschedule <= 0 || (nextschedule - now) <= 0)) {
parent.ses.execute(this);
} else {
parent.ses.schedule(this,
nextschedule - now,
TimeUnit.NANOSECONDS);
}
}
}
}
}
protected SingletonTaskContext context = new SingletonTaskContext();
protected Runnable task;
protected ScheduledExecutorService ses;
/**
* Construct a new SingletonTask for the given runnable. The context
* is used to manage the state of the task execution and can be shared
* by more than one instance of the runnable.
*
* @param ses
* @param task
*/
public SingletonTask(ScheduledExecutorService ses,
Runnable task) {
super();
this.task = task;
this.ses = ses;
}
/**
* Schedule the task to run if there's not already a task scheduled
* If there is such a task waiting that has not already started, it
* cancel that task and reschedule it to run at the given time. If the
* task is already started, it will cause the task to be rescheduled once
* it completes to run after delay from the time of reschedule.
*
* @param delay the delay in scheduling
* @param unit the timeunit of the delay
*/
public void reschedule(long delay, TimeUnit unit) {
boolean needQueue = true;
SingletonTaskWorker stw = null;
synchronized (context) {
if (context.taskRunning || context.taskShouldRun) {
if (context.taskRunning) {
// schedule to restart at the right time
if (delay > 0) {
long now = System.nanoTime();
long then =
now + TimeUnit.NANOSECONDS.convert(delay, unit);
context.waitingTask.nextschedule = then;
} else {
context.waitingTask.nextschedule = 0;
}
needQueue = false;
} else {
// cancel and requeue
context.waitingTask.canceled = true;
context.waitingTask = null;
}
}
context.taskShouldRun = true;
if (needQueue) {
stw = context.waitingTask = new SingletonTaskWorker(this);
}
}
if (needQueue) {
if (delay <= 0)
ses.execute(stw);
else
ses.schedule(stw, delay, unit);
}
}
}