blob: abbd359bc5076d55a473a34d703d6b4a77dc3288 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 android.filterfw.core;
import android.os.ConditionVariable;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @hide
*/
public class SyncRunner extends GraphRunner {
private Scheduler mScheduler = null;
private OnRunnerDoneListener mDoneListener = null;
private ScheduledThreadPoolExecutor mWakeExecutor = new ScheduledThreadPoolExecutor(1);
private ConditionVariable mWakeCondition = new ConditionVariable();
private StopWatchMap mTimer = null;
private final boolean mLogVerbose;
private final static String TAG = "SyncRunner";
// TODO: Provide factory based constructor?
public SyncRunner(FilterContext context, FilterGraph graph, Class schedulerClass) {
super(context);
mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
if (mLogVerbose) Log.v(TAG, "Initializing SyncRunner");
// Create the scheduler
if (Scheduler.class.isAssignableFrom(schedulerClass)) {
try {
Constructor schedulerConstructor = schedulerClass.getConstructor(FilterGraph.class);
mScheduler = (Scheduler)schedulerConstructor.newInstance(graph);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Scheduler does not have constructor <init>(FilterGraph)!", e);
} catch (InstantiationException e) {
throw new RuntimeException("Could not instantiate the Scheduler instance!", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access Scheduler constructor!", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Scheduler constructor threw an exception", e);
} catch (Exception e) {
throw new RuntimeException("Could not instantiate Scheduler", e);
}
} else {
throw new IllegalArgumentException("Class provided is not a Scheduler subclass!");
}
// Associate this runner and the graph with the context
mFilterContext = context;
mFilterContext.addGraph(graph);
mTimer = new StopWatchMap();
if (mLogVerbose) Log.v(TAG, "Setting up filters");
// Setup graph filters
graph.setupFilters();
}
@Override
public FilterGraph getGraph() {
return mScheduler != null ? mScheduler.getGraph() : null;
}
public int step() {
assertReadyToStep();
if (!getGraph().isReady() ) {
throw new RuntimeException("Trying to process graph that is not open!");
}
return performStep() ? RESULT_RUNNING : determinePostRunState();
}
public void beginProcessing() {
mScheduler.reset();
getGraph().beginProcessing();
}
public void close() {
// Close filters
if (mLogVerbose) Log.v(TAG, "Closing graph.");
getGraph().closeFilters(mFilterContext);
mScheduler.reset();
}
@Override
public void run() {
if (mLogVerbose) Log.v(TAG, "Beginning run.");
assertReadyToStep();
// Preparation
beginProcessing();
boolean glActivated = activateGlContext();
// Run
boolean keepRunning = true;
while (keepRunning) {
keepRunning = performStep();
}
// Cleanup
if (glActivated) {
deactivateGlContext();
}
// Call completion callback if set
if (mDoneListener != null) {
if (mLogVerbose) Log.v(TAG, "Calling completion listener.");
mDoneListener.onRunnerDone(determinePostRunState());
}
if (mLogVerbose) Log.v(TAG, "Run complete");
}
@Override
public boolean isRunning() {
return false;
}
@Override
public void setDoneCallback(OnRunnerDoneListener listener) {
mDoneListener = listener;
}
@Override
public void stop() {
throw new RuntimeException("SyncRunner does not support stopping a graph!");
}
@Override
synchronized public Exception getError() {
return null;
}
protected void waitUntilWake() {
mWakeCondition.block();
}
protected void processFilterNode(Filter filter) {
if (mLogVerbose) Log.v(TAG, "Processing filter node");
filter.performProcess(mFilterContext);
if (filter.getStatus() == Filter.STATUS_ERROR) {
throw new RuntimeException("There was an error executing " + filter + "!");
} else if (filter.getStatus() == Filter.STATUS_SLEEPING) {
if (mLogVerbose) Log.v(TAG, "Scheduling filter wakeup");
scheduleFilterWake(filter, filter.getSleepDelay());
}
}
protected void scheduleFilterWake(Filter filter, int delay) {
// Close the wake condition
mWakeCondition.close();
// Schedule the wake-up
final Filter filterToSchedule = filter;
final ConditionVariable conditionToWake = mWakeCondition;
mWakeExecutor.schedule(new Runnable() {
@Override
public void run() {
filterToSchedule.unsetStatus(Filter.STATUS_SLEEPING);
conditionToWake.open();
}
}, delay, TimeUnit.MILLISECONDS);
}
protected int determinePostRunState() {
boolean isBlocked = false;
for (Filter filter : mScheduler.getGraph().getFilters()) {
if (filter.isOpen()) {
if (filter.getStatus() == Filter.STATUS_SLEEPING) {
// If ANY node is sleeping, we return our state as sleeping
return RESULT_SLEEPING;
} else {
// If a node is still open, it is blocked (by input or output)
return RESULT_BLOCKED;
}
}
}
return RESULT_FINISHED;
}
// Core internal methods ///////////////////////////////////////////////////////////////////////
boolean performStep() {
if (mLogVerbose) Log.v(TAG, "Performing one step.");
Filter filter = mScheduler.scheduleNextNode();
if (filter != null) {
mTimer.start(filter.getName());
processFilterNode(filter);
mTimer.stop(filter.getName());
return true;
} else {
return false;
}
}
void assertReadyToStep() {
if (mScheduler == null) {
throw new RuntimeException("Attempting to run schedule with no scheduler in place!");
} else if (getGraph() == null) {
throw new RuntimeException("Calling step on scheduler with no graph in place!");
}
}
}