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

Commit 0283bdb0 authored by Eric Lin's avatar Eric Lin
Browse files

Disable ObjectPool in WM server transaction w/ flag.

ObjectPool is bypassed if the flag is enabled, letting the GC handle
lifecycle items. This addresses unnecessary overhead from synchronized
map operations and allows using the 'final' keyword on pool items for
further cleanup.

Benchmarking each test case (50,000 iterations) shows
~25% (193.77 ms) speedup with ~15% GC time (9.49 ms) tradeoff:
http://gpaste/5510735790866432

Bug: 311089192
Test: atest FrameworksCoreTests:ObjectPoolTests
Flag: com.android.window.flags.disable_object_pool
Change-Id: I1eb5de46e7b58ab5356bcdd4d9bb0c2fcf99efeb
parent 26b25da3
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -249,6 +249,9 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {

    @Override
    public void recycle() {
        if (Flags.disableObjectPool()) {
            return;
        }
        if (mTransactionItems != null) {
            int size = mTransactionItems.size();
            for (int i = 0; i < size; i++) {
+8 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.app.servertransaction;

import com.android.window.flags.Flags;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -38,6 +40,9 @@ class ObjectPool {
     * @return An instance or null if there is none.
     */
    public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
        if (Flags.disableObjectPool()) {
            return null;
        }
        synchronized (sPoolSync) {
            @SuppressWarnings("unchecked")
            final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
@@ -54,6 +59,9 @@ class ObjectPool {
     * @see ObjectPoolItem#recycle()
     */
    public static <T extends ObjectPoolItem> void recycle(T item) {
        if (Flags.disableObjectPool()) {
            return;
        }
        synchronized (sPoolSync) {
            @SuppressWarnings("unchecked")
            ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
+11 −0
Original line number Diff line number Diff line
@@ -169,3 +169,14 @@ flag {
         purpose: PURPOSE_BUGFIX
     }
}

flag {
    namespace: "windowing_sdk"
    name: "disable_object_pool"
    description: "Whether to disable object pool and let the GC handle lifecycle items"
    bug: "311089192"
    is_fixed_read_only: true
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ android_test {
        "kotlin-test",
        "mockito-target-minus-junit4",
        "androidx.test.uiautomator_uiautomator",
        "platform-parametric-runner-lib",
        "platform-test-annotations",
        "platform-compat-test-rules",
        "truth",
+33 −3
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;

import static com.android.window.flags.Flags.FLAG_DISABLE_OBJECT_POOL;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
@@ -39,19 +41,27 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.window.ActivityWindowInfo;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.window.flags.Flags;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;
import java.util.function.Supplier;

import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;

/**
 * Tests for {@link ObjectPool}.
 *
@@ -61,16 +71,28 @@ import java.util.function.Supplier;
 * <p>This test class is a part of Window Manager Service tests and specified in
 * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
 */
@RunWith(AndroidJUnit4.class)
@RunWith(ParameterizedAndroidJunit4.class)
@SmallTest
@Presubmit
public class ObjectPoolTests {

    @Parameters(name = "{0}")
    public static List<FlagsParameterization> getParams() {
        return FlagsParameterization.allCombinationsOf(FLAG_DISABLE_OBJECT_POOL);
    }

    @Rule
    public SetFlagsRule mSetFlagsRule;

    @Mock
    private IApplicationThread mApplicationThread;
    @Mock
    private IBinder mActivityToken;

    public ObjectPoolTests(FlagsParameterization flags) {
        mSetFlagsRule = new SetFlagsRule(flags);
    }

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
@@ -199,12 +221,20 @@ public class ObjectPoolTests {
        item.recycle();
        final ObjectPoolItem item2 = obtain.get();

        if (Flags.disableObjectPool()) {
            assertNotSame(item, item2);  // Different instance.
        } else {
            assertSame(item, item2);
        }

        // Create new object when the pool is empty.
        final ObjectPoolItem item3 = obtain.get();

        assertNotSame(item, item3);
        if (Flags.disableObjectPool()) {
            // Skip recycle if flag enabled, compare unnecessary.
            return;
        }
        assertEquals(item, item3);

        // Reset fields after recycle.