Bug 715164 - Guard against another race condition in PZC. r=pcwalton
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 05 Jan 2012 18:13:25 -0800
changeset 85137 91a8e4c55adbc9d8024cfe416d143446c291420b
parent 85136 b0ec2c8e105561269d27f1a9545f4b568064e2fd
child 85138 aae720a3208821564bd7ceed7a049f6c79d67c6f
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspcwalton
bugs715164
milestone12.0a1
Bug 715164 - Guard against another race condition in PZC. r=pcwalton
mobile/android/base/ui/PanZoomController.java
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -120,16 +120,18 @@ public class PanZoomController
         0.90651f,   /* 12 */
         0.94471f,   /* 13 */
         0.97401f,   /* 14 */
         0.99309f,   /* 15 */
     };
 
     /* The timer that handles flings or bounces. */
     private Timer mAnimationTimer;
+    /* The runnable being scheduled by the animation timer. */
+    private AnimationRunnable mAnimationRunnable;
     /* Information about the X axis. */
     private AxisX mX;
     /* Information about the Y axis. */
     private AxisY mY;
     /* The zoom focus at the first zoom event (in page coordinates). */
     private PointF mLastZoomFocus;
     /* The time the last motion event took place. */
     private long mLastEventTime;
@@ -526,35 +528,40 @@ public class PanZoomController
     }
 
     /* Performs a bounce-back animation to the nearest valid viewport metrics. */
     private void bounce() {
         bounce(getValidViewportMetrics());
     }
 
     /* Starts the fling or bounce animation. */
-    private void startAnimationTimer(final Runnable runnable) {
+    private void startAnimationTimer(final AnimationRunnable runnable) {
         if (mAnimationTimer != null) {
             Log.e(LOGTAG, "Attempted to start a new fling without canceling the old one!");
             stopAnimationTimer();
         }
 
         mAnimationTimer = new Timer("Animation Timer");
+        mAnimationRunnable = runnable;
         mAnimationTimer.scheduleAtFixedRate(new TimerTask() {
             @Override
             public void run() { mController.post(runnable); }
         }, 0, 1000L/60L);
     }
 
     /* Stops the fling or bounce animation. */
     private void stopAnimationTimer() {
         if (mAnimationTimer != null) {
             mAnimationTimer.cancel();
             mAnimationTimer = null;
         }
+        if (mAnimationRunnable != null) {
+            mAnimationRunnable.terminate();
+            mAnimationRunnable = null;
+        }
     }
 
     private boolean stopped() {
         float absVelocity = (float)Math.sqrt(mX.velocity * mX.velocity +
                                              mY.velocity * mY.velocity);
         return absVelocity < STOPPED_THRESHOLD;
     }
 
@@ -582,33 +589,58 @@ public class PanZoomController
             synchronized (mController) {
                 mController.scrollBy(new PointF(mX.displacement, mY.displacement));
             }
         }
 
         mX.displacement = mY.displacement = 0;
     }
 
+    private abstract class AnimationRunnable implements Runnable {
+        private boolean mAnimationTerminated;
+
+        /* This should always run on the UI thread */
+        public final void run() {
+            /*
+             * Since the animation timer queues this runnable on the UI thread, it
+             * is possible that even when the animation timer is cancelled, there
+             * are multiple instances of this queued, so we need to have another
+             * mechanism to abort. This is done by using the mAnimationTerminated flag.
+             */
+            if (mAnimationTerminated) {
+                return;
+            }
+            animateFrame();
+        }
+
+        protected abstract void animateFrame();
+
+        /* This should always run on the UI thread */
+        protected final void terminate() {
+            mAnimationTerminated = true;
+        }
+    }
+
     /* The callback that performs the bounce animation. */
-    private class BounceRunnable implements Runnable {
+    private class BounceRunnable extends AnimationRunnable {
         /* The current frame of the bounce-back animation */
         private int mBounceFrame;
         /*
          * The viewport metrics that represent the start and end of the bounce-back animation,
          * respectively.
          */
         private ViewportMetrics mBounceStartMetrics;
         private ViewportMetrics mBounceEndMetrics;
 
         BounceRunnable(ViewportMetrics startMetrics, ViewportMetrics endMetrics) {
             mBounceStartMetrics = startMetrics;
             mBounceEndMetrics = endMetrics;
         }
 
-        public void run() {
+        protected void animateFrame() {
             /*
              * The pan/zoom controller might have signaled to us that it wants to abort the
              * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
              * out.
              */
             if (mState != PanZoomState.FLING) {
                 finishAnimation();
                 return;
@@ -643,18 +675,18 @@ public class PanZoomController
                 mController.setViewportMetrics(mBounceEndMetrics);
                 mController.notifyLayerClientOfGeometryChange();
                 mBounceFrame = -1;
             }
         }
     }
 
     // The callback that performs the fling animation.
-    private class FlingRunnable implements Runnable {
-        public void run() {
+    private class FlingRunnable extends AnimationRunnable {
+        protected void animateFrame() {
             /*
              * The pan/zoom controller might have signaled to us that it wants to abort the
              * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
              * out.
              */
             if (mState != PanZoomState.FLING) {
                 finishAnimation();
                 return;