/* * 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 (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!"); } } }