package org.commcare.utils;
/**
* A time bound operation will run a synchronized operation on a blocking thread with the confidence
* that it will either execute within the time frame provided, or it will not fire its resolution
* method, and will return control to the main thead after the timeout has expired.
*
* The operation should consist of two steps: A first step that should have no side effects
* (run), and an optional second step (commit) that executes after the first is
* completed. The second step will not be bounded, but is guaranteed to not execute if the first
* step did not complete in time.
*
* A time bound operation can only be executed once.
*
* Created by ctsims on 4/4/2016.
*/
public abstract class TimeBoundOperation {
final private long timeout;
private boolean hasExecuted;
public TimeBoundOperation(long timeout) {
this.timeout = timeout;
}
/**
* The operation which will be executed within the timeout limit. Should not contain side
* effects which will effect runtime if they execute indefinitely
*/
protected abstract void run();
/**
* The optional commit step after the run executes which isn't bounded, and can have
* side effects that will be guaranteed to run if the execution succeeds
*/
protected void commit() {
}
/**
* Executes the operation and optionally the commit step if the run step succeeds in time
*
* @return true if both phases of the timed operation succeeded. False if the operaiton
* timed out and the commit step did not run.
*
* @throws IllegalStateException If the operation has already been executed
*/
public synchronized boolean execute() throws IllegalStateException {
if(hasExecuted) {
throw new IllegalStateException("time bound operation has already executed");
}
hasExecuted = true;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
TimeBoundOperation.this.run();
}
});
thread.start();
try {
thread.join(timeout);
}catch (InterruptedException e) {
//Execution failed
return false;
}
commit();
return true;
}
}