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

Commit 385a9fe0 authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Fix cross-thread access to collection in ViewRootImpl" into main

parents d749a1e0 9e706666
Loading
Loading
Loading
Loading
+41 −15
Original line number Original line Diff line number Diff line
@@ -671,6 +671,7 @@ public final class ViewRootImpl implements ViewParent,
    private boolean mInvalidationIdleMessagePosted = false;
    private boolean mInvalidationIdleMessagePosted = false;
    // VRR: List of all Views that are animating with the threaded render
    // VRR: List of all Views that are animating with the threaded render
    private ArrayList<View> mThreadedRendererViews = new ArrayList();
    private ArrayList<View> mThreadedRendererViews = new ArrayList();
    private ArrayList<View> mThreadedRendererViewsCache = new ArrayList();
    /**
    /**
     * Update the Choreographer's FrameInfo object with the timing information for the current
     * Update the Choreographer's FrameInfo object with the timing information for the current
@@ -4548,10 +4549,7 @@ public final class ViewRootImpl implements ViewParent,
            }
            }
            mDrawnThisFrame = false;
            mDrawnThisFrame = false;
            if (!mInvalidationIdleMessagePosted) {
            sendCheckInvalidationIdle();
                mInvalidationIdleMessagePosted = true;
                mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
            }
            setCategoryFromCategoryCounts();
            setCategoryFromCategoryCounts();
            updateInfrequentCount();
            updateInfrequentCount();
            updateFrameRateFromThreadedRendererViews();
            updateFrameRateFromThreadedRendererViews();
@@ -4584,6 +4582,19 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
    }
    }
    private void sendCheckInvalidationIdle() {
        if (shouldEnableDvrr()) {
            boolean wasPosted;
            synchronized (mThreadedRendererViews) {
                wasPosted = mInvalidationIdleMessagePosted;
                mInvalidationIdleMessagePosted = true;
            }
            if (!wasPosted) {
                mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
            }
        }
    }
    private void createSyncIfNeeded() {
    private void createSyncIfNeeded() {
        // WMS requested sync already started or there's nothing needing to sync
        // WMS requested sync already started or there's nothing needing to sync
        if (isInWMSRequestedSync() || !mReportNextDraw) {
        if (isInWMSRequestedSync() || !mReportNextDraw) {
@@ -13182,16 +13193,22 @@ public final class ViewRootImpl implements ViewParent,
     * from those views.
     * from those views.
     */
     */
    private void updateFrameRateFromThreadedRendererViews() {
    private void updateFrameRateFromThreadedRendererViews() {
        ArrayList<View> views = mThreadedRendererViews;
        ArrayList<View> views = mThreadedRendererViewsCache;
        synchronized (mThreadedRendererViews) {
            views.addAll(mThreadedRendererViews);
        }
        for (int i = views.size() - 1; i >= 0; i--) {
        for (int i = views.size() - 1; i >= 0; i--) {
            View view = views.get(i);
            View view = views.get(i);
            View.AttachInfo attachInfo = view.mAttachInfo;
            View.AttachInfo attachInfo = view.mAttachInfo;
            if (attachInfo == null || attachInfo.mViewRootImpl != this) {
            if (attachInfo == null || attachInfo.mViewRootImpl != this) {
                views.remove(i);
                synchronized (mThreadedRendererViews) {
                    mThreadedRendererViews.remove(view);
                }
            } else {
            } else {
                view.votePreferredFrameRate();
                view.votePreferredFrameRate();
            }
            }
        }
        }
        views.clear();
    }
    }
    /**
    /**
@@ -13400,10 +13417,14 @@ public final class ViewRootImpl implements ViewParent,
     * @param view The View with the ThreadedRenderer animation that started.
     * @param view The View with the ThreadedRenderer animation that started.
     */
     */
    public void addThreadedRendererView(View view) {
    public void addThreadedRendererView(View view) {
        if (shouldEnableDvrr() && !mThreadedRendererViews.contains(view)) {
        if (shouldEnableDvrr()) {
            synchronized (mThreadedRendererViews) {
                if (!mThreadedRendererViews.contains(view)) {
                    mThreadedRendererViews.add(view);
                    mThreadedRendererViews.add(view);
                }
                }
            }
            }
        }
    }
    /**
    /**
     * When a ThreadedRenderer animation ends, the View that is associated with it using
     * When a ThreadedRenderer animation ends, the View that is associated with it using
@@ -13411,10 +13432,11 @@ public final class ViewRootImpl implements ViewParent,
     * @param view The View whose ThreadedRender animation has stopped.
     * @param view The View whose ThreadedRender animation has stopped.
     */
     */
    public void removeThreadedRendererView(View view) {
    public void removeThreadedRendererView(View view) {
        if (shouldEnableDvrr()) {
            synchronized (mThreadedRendererViews) {
                mThreadedRendererViews.remove(view);
                mThreadedRendererViews.remove(view);
        if (shouldEnableDvrr() && !mInvalidationIdleMessagePosted) {
            }
            mInvalidationIdleMessagePosted = true;
            sendCheckInvalidationIdle();
            mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
        }
        }
    }
    }
@@ -13633,10 +13655,10 @@ public final class ViewRootImpl implements ViewParent,
        mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
        mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
        mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
        mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
        mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
        mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
        if (mInvalidationIdleMessagePosted) {
        synchronized (mThreadedRendererViews) {
            mInvalidationIdleMessagePosted = false;
            mInvalidationIdleMessagePosted = false;
            mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
        }
        }
        mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
    }
    }
    /**
    /**
@@ -13655,7 +13677,11 @@ public final class ViewRootImpl implements ViewParent,
        mMinusOneFrameIntervalMillis = timeIntervalMillis;
        mMinusOneFrameIntervalMillis = timeIntervalMillis;
        mLastUpdateTimeMillis = currentTimeMillis;
        mLastUpdateTimeMillis = currentTimeMillis;
        if (mThreadedRendererViews.isEmpty() && timeIntervalMillis + mMinusTwoFrameIntervalMillis
        boolean isThreadedRendererViewsEmpty;
        synchronized (mThreadedRendererViews) {
            isThreadedRendererViewsEmpty = mThreadedRendererViews.isEmpty();
        }
        if (isThreadedRendererViewsEmpty && timeIntervalMillis + mMinusTwoFrameIntervalMillis
                >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
                >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
            int infrequentUpdateCount = mInfrequentUpdateCount;
            int infrequentUpdateCount = mInfrequentUpdateCount;
            mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
            mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
+35 −0
Original line number Original line Diff line number Diff line
@@ -106,6 +106,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
/**
 * Tests for {@link ViewRootImpl}
 * Tests for {@link ViewRootImpl}
@@ -1628,6 +1629,40 @@ public class ViewRootImplTest {
        assertThat(bounds.height()).isAtLeast(strokeWidth * 2);
        assertThat(bounds.height()).isAtLeast(strokeWidth * 2);
    }
    }


    @Test
    public void testOffThreadRendererViewsAccess() throws Throwable {
        mView = new View(sContext);
        attachViewToWindow(mView);
        AtomicInteger threadRunning = new AtomicInteger(1);
        View[] views = new View[10];
        for (int i = 0; i < 10; i++) {
            views[i] = new View(sContext);
        }
        Thread offThread = new Thread(() -> {
            while (threadRunning.get() > 0) {
                for (int i = 0; i < 10; i++) {
                    mViewRootImpl.addThreadedRendererView(views[i]);
                }
                for (int i = 0; i < 10; i++) {
                    mViewRootImpl.removeThreadedRendererView(views[i]);
                }
            }
        });
        offThread.start();
        try {
            for (int i = 0; i < 1000; i++) {
                sInstrumentation.runOnMainSync(() -> {
                    mView.invalidate();
                    runAfterDraw(() -> {
                    });
                });
                waitForAfterDraw();
            }
        } finally {
            threadRunning.set(0);
        }
    }

    static class InputView extends View {
    static class InputView extends View {
        private final BlockingQueue<InputEvent> mEvents = new LinkedBlockingQueue<>();
        private final BlockingQueue<InputEvent> mEvents = new LinkedBlockingQueue<>();
        private final BlockingQueueEventVerifier mVerifier =
        private final BlockingQueueEventVerifier mVerifier =