Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ab12ba8 authored by Simon Bowden's avatar Simon Bowden
Browse files

Fix a race condition between calculateWaitTime and actually waiting.

When the race was lost with a completion callback, the operation
being waited upon may have been de-queued by the completion callback
for immediate execution, but the thread was still proceeding with
the wait.

This caused flaky tests as the extra delay caused the thread to
take longer to complete, and the newly extended callback delay
happened to coincide with the test's wait timeout. Consequently,
the test's wait timeout is shortened slightly to make it clearer
when the issue is caused by "falling into" the OffStep callback
delay, which isn't intended to be a normal behavior.

Test: manual atest
Bug: 194408223
Change-Id: I97ec74bbc26171a4384ed9ddd7e2130fb8004b9b
parent 5528d08a
Loading
Loading
Loading
Loading
+17 −13
Original line number Diff line number Diff line
@@ -228,17 +228,20 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {

            Vibration.Status status = null;
            while (!mStepQueue.isEmpty()) {
                long waitTime = mStepQueue.calculateWaitTime();
                if (waitTime <= 0) {
                    mStepQueue.consumeNext();
                } else {
                long waitTime;
                synchronized (mLock) {
                    waitTime = mStepQueue.calculateWaitTime();
                    if (waitTime > 0) {
                        try {
                            mLock.wait(waitTime);
                        } catch (InterruptedException e) {
                        }
                    }
                }
                // If we waited, the queue may have changed, so let the loop run again.
                if (waitTime <= 0) {
                    mStepQueue.consumeNext();
                }
                Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED
                        : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
                if (status == null && currentStatus != Vibration.Status.RUNNING) {
@@ -387,15 +390,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
        }

        /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
        @GuardedBy("mLock")
        public long calculateWaitTime() {
            Step nextStep;
            synchronized (mLock) {
            if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
                // Steps anticipated by vibrator complete callback should be played right away.
                return 0;
            }
                nextStep = mNextSteps.peek();
            }
            Step nextStep = mNextSteps.peek();
            return nextStep == null ? 0 : nextStep.calculateWaitTime();
        }

@@ -603,7 +604,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
            return false;
        }

        /** Returns the time in millis to wait before playing this step. */
        /**
         * Returns the time in millis to wait before playing this step. This is performed
         * while holding the queue lock, so should not rely on potentially slow operations.
         */
        public long calculateWaitTime() {
            if (startTime == Long.MAX_VALUE) {
                // This step don't have a predefined start time, it's just marked to be executed
+1 −1
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ import java.util.stream.Collectors;
@Presubmit
public class VibrationThreadTest {

    private static final int TEST_TIMEOUT_MILLIS = 1_000;
    private static final int TEST_TIMEOUT_MILLIS = 900;
    private static final int UID = Process.ROOT_UID;
    private static final int VIBRATOR_ID = 1;
    private static final String PACKAGE_NAME = "package";