package org.andork.tracker; import java.util.ArrayList; import java.util.List; public class Computation implements Runnable { Tracker tracker; ComputeFunction func; boolean stopped = false; boolean invalidated = false; boolean firstRun = true; boolean recomputing = false; final List<Runnable> onInvalidateCallbacks = new ArrayList<Runnable>(); final List<Runnable> onStopCallbacks = new ArrayList<Runnable>(); Computation(Tracker tracker, ComputeFunction r) { this.tracker = tracker; func = r; } void compute() throws Exception { invalidated = false; Computation previous = Tracker.currentComputation(); Tracker.setCurrentComputation(this); boolean previousInCompute = tracker.inCompute(); tracker.setInCompute(true); try { func.run(this); } finally { Tracker.setCurrentComputation(previous); tracker.setInCompute(previousInCompute); } } public void flush() { if (recomputing) { return; } recompute(); } public void invalidate() { // if we're currently in _recompute(), don't enqueue // ourselves, since we'll rerun immediately anyway. if (invalidated) { return; } invalidated = true; if (!recomputing && !stopped) { tracker.requireFlush(); tracker.addPendingComputation(this); } // callbacks can't add callbacks, because // 280 // self.invalidated === true. for (Runnable callback : onInvalidateCallbacks) { tracker.nonreactive(callback); } onInvalidateCallbacks.clear(); } boolean needsRecompute() { return invalidated && !stopped; } public void onInvalidate(Runnable r) { if (invalidated) { tracker.nonreactive(r); } else { onInvalidateCallbacks.add(r); } } public void onStop(Runnable r) { if (stopped) { tracker.nonreactive(r); } else { onStopCallbacks.add(r); } } void recompute() { recomputing = true; try { if (needsRecompute()) { try { compute(); } catch (Exception e) { tracker.throwOrLog("recompute", e); } } } finally { recomputing = false; } } @Override public void run() { invalidate(); flush(); } void start() throws Exception { boolean errored = true; try { compute(); errored = false; } finally { firstRun = false; if (errored) { stop(); } } } public void stop() { if (stopped) { return; } stopped = true; invalidate(); for (Runnable callback : onStopCallbacks) { tracker.nonreactive(callback); } onStopCallbacks.clear(); } }