Home | History | Annotate | Download | only in core
      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?
     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
     85     public FilterGraph getGraph() {
     86         return mScheduler != null ? mScheduler.getGraph() : null;
     87     }
     88 
     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 
     97     public void beginProcessing() {
     98         mScheduler.reset();
     99         getGraph().beginProcessing();
    100     }
    101 
    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
    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
    139     public boolean isRunning() {
    140         return false;
    141     }
    142 
    143     @Override
    144     public void setDoneCallback(OnRunnerDoneListener listener) {
    145         mDoneListener = listener;
    146     }
    147 
    148     @Override
    149     public void stop() {
    150         throw new RuntimeException("SyncRunner does not support stopping a graph!");
    151     }
    152 
    153     @Override
    154     synchronized public Exception getError() {
    155         return null;
    156     }
    157 
    158     protected void waitUntilWake() {
    159         mWakeCondition.block();
    160     }
    161 
    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 
    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 
    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 ///////////////////////////////////////////////////////////////////////
    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 
    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