Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt 0 → 100644 +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() } } libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +80 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } } libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +3 −3 Original line number Diff line number Diff line Loading @@ -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, Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md +2 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/BoostExecutor.kt 0 → 100644 +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() } }
libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +80 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +3 −3 Original line number Diff line number Diff line Loading @@ -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, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md +2 −1 Original line number Diff line number Diff line Loading @@ -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