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

Commit b0e5e5b4 authored by Winson Chung's avatar Winson Chung
Browse files

Add mechanism to boost the shell background thread as needed

- This will be used when things offloaded to the BG thread need to
  be boosted in thread priority (ie. when loading icons along
  critical paths)

Bug: 370895085
Flag: EXEMPT bugfix
Test: atest WMShellUnitTests:HandlerExecutorTest
Change-Id: I531922a2f7b993df1ce4723b4b313bb070f728e7
parent 471e6c9e
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.wm.shell.common

import android.os.Looper
import java.util.concurrent.Executor

/** Executor implementation which can be boosted temporarily to a different thread priority.  */
interface BoostExecutor : Executor {
    /**
     * Requests that the executor is boosted until {@link #resetBoost()} is called.
     */
    fun setBoost() {}

    /**
     * Requests that the executor is not boosted (only resets if there are no other boost requests
     * in progress).
     */
    fun resetBoost() {}

    /**
     * Returns whether the executor is boosted.
     */
    fun isBoosted() : Boolean {
        return false
    }

    /**
     * Returns the looper for this executor.
     */
    fun getLooper() : Looper? {
        return Looper.myLooper()
    }
}
+80 −0
Original line number Diff line number Diff line
@@ -16,15 +16,50 @@

package com.android.wm.shell.common;

import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import static android.os.Process.setThreadPriority;

import android.annotation.NonNull;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;

import androidx.annotation.VisibleForTesting;

import java.util.function.BiConsumer;

/** Executor implementation which is backed by a Handler. */
public class HandlerExecutor implements ShellExecutor {
    @NonNull
    private final Handler mHandler;
    // See android.os.Process#THREAD_PRIORITY_*
    private final int mDefaultThreadPriority;
    private final int mBoostedThreadPriority;
    // Number of current requests to boost thread priority
    private int mBoostCount;
    private final Object mBoostLock = new Object();
    // Default function for setting thread priority (tid, priority)
    private BiConsumer<Integer, Integer> mSetThreadPriorityFn =
            HandlerExecutor::setThreadPriorityInternal;

    public HandlerExecutor(@NonNull Handler handler) {
        this(handler, THREAD_PRIORITY_DEFAULT, THREAD_PRIORITY_DEFAULT);
    }

    /**
     * Used only if this executor can be boosted, if so, it can be boosted to the given
     * {@param boostPriority}.
     */
    public HandlerExecutor(@NonNull Handler handler, int defaultThreadPriority,
            int boostedThreadPriority) {
        mHandler = handler;
        mDefaultThreadPriority = defaultThreadPriority;
        mBoostedThreadPriority = boostedThreadPriority;
    }

    @VisibleForTesting
    void replaceSetThreadPriorityFn(BiConsumer<Integer, Integer> setThreadPriorityFn) {
        mSetThreadPriorityFn = setThreadPriorityFn;
    }

    @Override
@@ -55,10 +90,55 @@ public class HandlerExecutor implements ShellExecutor {
        return mHandler.hasCallbacks(r);
    }

    @Override
    public void setBoost() {
        synchronized (mBoostLock) {
            if (mDefaultThreadPriority == mBoostedThreadPriority) {
                // Nothing to boost
                return;
            }
            if (mBoostCount == 0) {
                mSetThreadPriorityFn.accept(
                        ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
                        mBoostedThreadPriority);
            }
            mBoostCount++;
        }
    }

    @Override
    public void resetBoost() {
        synchronized (mBoostLock) {
            mBoostCount--;
            if (mBoostCount == 0) {
                mSetThreadPriorityFn.accept(
                        ((HandlerThread) mHandler.getLooper().getThread()).getThreadId(),
                        mDefaultThreadPriority);
            }
        }
    }

    @Override
    public boolean isBoosted() {
        synchronized (mBoostLock) {
            return mBoostCount > 0;
        }
    }

    @Override
    @NonNull
    public Looper getLooper() {
        return mHandler.getLooper();
    }

    @Override
    public void assertCurrentThread() {
        if (!mHandler.getLooper().isCurrentThread()) {
            throw new IllegalStateException("must be called on " + mHandler);
        }
    }

    private static void setThreadPriorityInternal(Integer tid, Integer priority) {
        setThreadPriority(tid, priority);
    }
}
+3 −3
Original line number Diff line number Diff line
@@ -18,15 +18,15 @@ package com.android.wm.shell.common;

import java.lang.reflect.Array;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Super basic Executor interface that adds support for delayed execution and removing callbacks.
 * Intended to wrap Handler while better-supporting testing.
 * Intended to wrap Handler while better-supporting testing.  Not every ShellExecutor implementation
 * may support boosting.
 */
public interface ShellExecutor extends Executor {
public interface ShellExecutor extends BoostExecutor {

    /**
     * Executes the given runnable. If the caller is running on the same looper as this executor,
+4 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.dagger;

import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;

import android.content.Context;
@@ -205,13 +206,14 @@ public abstract class WMShellConcurrencyModule {
    }

    /**
     * Provides a Shell background thread Executor for low priority background tasks.
     * Provides a Shell background thread Executor for low priority background tasks.  The thread
     * may also be boosted to THREAD_PRIORITY_FOREGROUND if necessary.
     */
    @WMSingleton
    @Provides
    @ShellBackgroundThread
    public static ShellExecutor provideSharedBackgroundExecutor(
            @ShellBackgroundThread Handler handler) {
        return new HandlerExecutor(handler);
        return new HandlerExecutor(handler, THREAD_PRIORITY_BACKGROUND, THREAD_PRIORITY_FOREGROUND);
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ the product.
  thread)
  - This is always another thread even if config_enableShellMainThread is not set true
  - **Note**:
    - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
    - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority but can be requested to be boosted
      to `THREAD_PRIORITY_FOREGROUND`
- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
  animations could be offloaded here)
- `ShellSplashScreenThread` (only for use with splashscreens)
Loading