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

Commit 283cd08f authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Add mechanism to boost the shell background thread as needed" into main

parents 363ab6af b0e5e5b4
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