1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package android.filterfw.core; 19 20 import android.os.ConditionVariable; 21 import android.util.Log; 22 23 import java.lang.reflect.Constructor; 24 import java.lang.reflect.InvocationTargetException; 25 import java.util.concurrent.ScheduledThreadPoolExecutor; 26 import java.util.concurrent.TimeUnit; 27 28 /** 29 * @hide 30 */ 31 public class SyncRunner extends GraphRunner { 32 33 private Scheduler mScheduler = null; 34 35 private OnRunnerDoneListener mDoneListener = null; 36 private ScheduledThreadPoolExecutor mWakeExecutor = new ScheduledThreadPoolExecutor(1); 37 private ConditionVariable mWakeCondition = new ConditionVariable(); 38 39 private StopWatchMap mTimer = null; 40 41 private final boolean mLogVerbose; 42 private final static String TAG = "SyncRunner"; 43 44 // TODO: Provide factory based constructor? SyncRunner(FilterContext context, FilterGraph graph, Class schedulerClass)45 public SyncRunner(FilterContext context, FilterGraph graph, Class schedulerClass) { 46 super(context); 47 48 mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE); 49 50 if (mLogVerbose) Log.v(TAG, "Initializing SyncRunner"); 51 52 // Create the scheduler 53 if (Scheduler.class.isAssignableFrom(schedulerClass)) { 54 try { 55 Constructor schedulerConstructor = schedulerClass.getConstructor(FilterGraph.class); 56 mScheduler = (Scheduler)schedulerConstructor.newInstance(graph); 57 } catch (NoSuchMethodException e) { 58 throw new RuntimeException("Scheduler does not have constructor <init>(FilterGraph)!", e); 59 } catch (InstantiationException e) { 60 throw new RuntimeException("Could not instantiate the Scheduler instance!", e); 61 } catch (IllegalAccessException e) { 62 throw new RuntimeException("Cannot access Scheduler constructor!", e); 63 } catch (InvocationTargetException e) { 64 throw new RuntimeException("Scheduler constructor threw an exception", e); 65 } catch (Exception e) { 66 throw new RuntimeException("Could not instantiate Scheduler", e); 67 } 68 } else { 69 throw new IllegalArgumentException("Class provided is not a Scheduler subclass!"); 70 } 71 72 // Associate this runner and the graph with the context 73 mFilterContext = context; 74 mFilterContext.addGraph(graph); 75 76 mTimer = new StopWatchMap(); 77 78 if (mLogVerbose) Log.v(TAG, "Setting up filters"); 79 80 // Setup graph filters 81 graph.setupFilters(); 82 } 83 84 @Override getGraph()85 public FilterGraph getGraph() { 86 return mScheduler != null ? mScheduler.getGraph() : null; 87 } 88 step()89 public int step() { 90 assertReadyToStep(); 91 if (!getGraph().isReady() ) { 92 throw new RuntimeException("Trying to process graph that is not open!"); 93 } 94 return performStep() ? RESULT_RUNNING : determinePostRunState(); 95 } 96 beginProcessing()97 public void beginProcessing() { 98 mScheduler.reset(); 99 getGraph().beginProcessing(); 100 } 101 close()102 public void close() { 103 // Close filters 104 if (mLogVerbose) Log.v(TAG, "Closing graph."); 105 getGraph().closeFilters(mFilterContext); 106 mScheduler.reset(); 107 } 108 109 @Override run()110 public void run() { 111 if (mLogVerbose) Log.v(TAG, "Beginning run."); 112 113 assertReadyToStep(); 114 115 // Preparation 116 beginProcessing(); 117 boolean glActivated = activateGlContext(); 118 119 // Run 120 boolean keepRunning = true; 121 while (keepRunning) { 122 keepRunning = performStep(); 123 } 124 125 // Cleanup 126 if (glActivated) { 127 deactivateGlContext(); 128 } 129 130 // Call completion callback if set 131 if (mDoneListener != null) { 132 if (mLogVerbose) Log.v(TAG, "Calling completion listener."); 133 mDoneListener.onRunnerDone(determinePostRunState()); 134 } 135 if (mLogVerbose) Log.v(TAG, "Run complete"); 136 } 137 138 @Override isRunning()139 public boolean isRunning() { 140 return false; 141 } 142 143 @Override setDoneCallback(OnRunnerDoneListener listener)144 public void setDoneCallback(OnRunnerDoneListener listener) { 145 mDoneListener = listener; 146 } 147 148 @Override stop()149 public void stop() { 150 throw new RuntimeException("SyncRunner does not support stopping a graph!"); 151 } 152 153 @Override getError()154 synchronized public Exception getError() { 155 return null; 156 } 157 waitUntilWake()158 protected void waitUntilWake() { 159 mWakeCondition.block(); 160 } 161 processFilterNode(Filter filter)162 protected void processFilterNode(Filter filter) { 163 if (mLogVerbose) Log.v(TAG, "Processing filter node"); 164 filter.performProcess(mFilterContext); 165 if (filter.getStatus() == Filter.STATUS_ERROR) { 166 throw new RuntimeException("There was an error executing " + filter + "!"); 167 } else if (filter.getStatus() == Filter.STATUS_SLEEPING) { 168 if (mLogVerbose) Log.v(TAG, "Scheduling filter wakeup"); 169 scheduleFilterWake(filter, filter.getSleepDelay()); 170 } 171 } 172 scheduleFilterWake(Filter filter, int delay)173 protected void scheduleFilterWake(Filter filter, int delay) { 174 // Close the wake condition 175 mWakeCondition.close(); 176 177 // Schedule the wake-up 178 final Filter filterToSchedule = filter; 179 final ConditionVariable conditionToWake = mWakeCondition; 180 181 mWakeExecutor.schedule(new Runnable() { 182 @Override 183 public void run() { 184 filterToSchedule.unsetStatus(Filter.STATUS_SLEEPING); 185 conditionToWake.open(); 186 } 187 }, delay, TimeUnit.MILLISECONDS); 188 } 189 determinePostRunState()190 protected int determinePostRunState() { 191 boolean isBlocked = false; 192 for (Filter filter : mScheduler.getGraph().getFilters()) { 193 if (filter.isOpen()) { 194 if (filter.getStatus() == Filter.STATUS_SLEEPING) { 195 // If ANY node is sleeping, we return our state as sleeping 196 return RESULT_SLEEPING; 197 } else { 198 // If a node is still open, it is blocked (by input or output) 199 return RESULT_BLOCKED; 200 } 201 } 202 } 203 return RESULT_FINISHED; 204 } 205 206 // Core internal methods /////////////////////////////////////////////////////////////////////// performStep()207 boolean performStep() { 208 if (mLogVerbose) Log.v(TAG, "Performing one step."); 209 Filter filter = mScheduler.scheduleNextNode(); 210 if (filter != null) { 211 mTimer.start(filter.getName()); 212 processFilterNode(filter); 213 mTimer.stop(filter.getName()); 214 return true; 215 } else { 216 return false; 217 } 218 } 219 assertReadyToStep()220 void assertReadyToStep() { 221 if (mScheduler == null) { 222 throw new RuntimeException("Attempting to run schedule with no scheduler in place!"); 223 } else if (getGraph() == null) { 224 throw new RuntimeException("Calling step on scheduler with no graph in place!"); 225 } 226 } 227 } 228