Reduce system calls of ThreadPriorityBooster
- ThreadLocal is bound to each thread, so we don't need to
get tid every time.
- By storing whether the priority is changed, we don't need
to get thread priority to decide whether to restore.
The cost of a simple boost region is reduced over 20%.
Test: atest ThreadPriorityBoosterTest
Bug: 123043091
Change-Id: I634898c1bf0d10de4f53fd18043f64d18fe9082b
diff --git a/services/core/java/com/android/server/ThreadPriorityBooster.java b/services/core/java/com/android/server/ThreadPriorityBooster.java
index f74a4385..dab6bc4 100644
--- a/services/core/java/com/android/server/ThreadPriorityBooster.java
+++ b/services/core/java/com/android/server/ThreadPriorityBooster.java
@@ -26,6 +26,7 @@
public class ThreadPriorityBooster {
private static final boolean ENABLE_LOCK_GUARD = false;
+ private static final int PRIORITY_NOT_ADJUSTED = Integer.MAX_VALUE;
private volatile int mBoostToPriority;
private final int mLockGuardIndex;
@@ -42,13 +43,12 @@
}
public void boost() {
- final int tid = myTid();
final PriorityState state = mThreadState.get();
if (state.regionCounter == 0) {
- final int prevPriority = getThreadPriority(tid);
- state.prevPriority = prevPriority;
+ final int prevPriority = getThreadPriority(state.tid);
if (prevPriority > mBoostToPriority) {
- setThreadPriority(tid, mBoostToPriority);
+ setThreadPriority(state.tid, mBoostToPriority);
+ state.prevPriority = prevPriority;
}
}
state.regionCounter++;
@@ -60,11 +60,9 @@
public void reset() {
final PriorityState state = mThreadState.get();
state.regionCounter--;
- if (state.regionCounter == 0) {
- final int currentPriority = getThreadPriority(myTid());
- if (state.prevPriority != currentPriority) {
- setThreadPriority(myTid(), state.prevPriority);
- }
+ if (state.regionCounter == 0 && state.prevPriority != PRIORITY_NOT_ADJUSTED) {
+ setThreadPriority(state.tid, state.prevPriority);
+ state.prevPriority = PRIORITY_NOT_ADJUSTED;
}
}
@@ -78,16 +76,16 @@
// variable immediately.
mBoostToPriority = priority;
final PriorityState state = mThreadState.get();
- final int tid = myTid();
if (state.regionCounter != 0) {
- final int prevPriority = getThreadPriority(tid);
+ final int prevPriority = getThreadPriority(state.tid);
if (prevPriority != priority) {
- setThreadPriority(tid, priority);
+ setThreadPriority(state.tid, priority);
}
}
}
private static class PriorityState {
+ final int tid = myTid();
/**
* Acts as counter for number of synchronized region that needs to acquire 'this' as a lock
@@ -99,6 +97,6 @@
/**
* The thread's previous priority before boosting.
*/
- int prevPriority;
+ int prevPriority = PRIORITY_NOT_ADJUSTED;
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java b/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java
new file mode 100644
index 0000000..b4e9ff7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/ThreadPriorityBoosterTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 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.server;
+
+import static android.os.Process.getThreadPriority;
+import static android.os.Process.myTid;
+import static android.os.Process.setThreadPriority;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link ThreadPriorityBooster}.
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:ThreadPriorityBoosterTest
+ */
+@SmallTest
+@Presubmit
+public class ThreadPriorityBoosterTest {
+ private static final int PRIORITY_BOOST = Process.THREAD_PRIORITY_FOREGROUND;
+ private static final int PRIORITY_BOOST_MORE = Process.THREAD_PRIORITY_DISPLAY;
+
+ private final ThreadPriorityBooster mBooster = new ThreadPriorityBooster(PRIORITY_BOOST,
+ 0 /* lockGuardIndex */);
+
+ @Test
+ public void testThreadPriorityBooster() {
+ joinNewThread(() -> {
+ final int origPriority = Process.THREAD_PRIORITY_DEFAULT;
+ setThreadPriority(origPriority);
+
+ boost(() -> {
+ assertThreadPriority(PRIORITY_BOOST);
+ boost(() -> {
+ // Inside the boost region, the priority should also apply to current thread.
+ mBooster.setBoostToPriority(PRIORITY_BOOST_MORE);
+ assertThreadPriority(PRIORITY_BOOST_MORE);
+ });
+ // It is still in the boost region so the set priority should be kept.
+ assertThreadPriority(PRIORITY_BOOST_MORE);
+
+ joinNewThread(() -> boost(() -> assertThreadPriority(PRIORITY_BOOST_MORE)));
+ });
+ // The priority should be restored after leaving the boost region.
+ assertThreadPriority(origPriority);
+
+ // It doesn't apply to current thread because outside of the boost region, but the boost
+ // in other threads will use the set priority.
+ mBooster.setBoostToPriority(PRIORITY_BOOST);
+ joinNewThread(() -> boost(() -> assertThreadPriority(PRIORITY_BOOST)));
+
+ assertThreadPriority(origPriority);
+ });
+ }
+
+ private static void assertThreadPriority(int expectedPriority) {
+ assertEquals(expectedPriority, getThreadPriority(myTid()));
+ }
+
+ private static void joinNewThread(Runnable action) {
+ final Thread thread = new Thread(action);
+ thread.start();
+ try {
+ thread.join();
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ private void boost(Runnable action) {
+ try {
+ mBooster.boost();
+ action.run();
+ } finally {
+ mBooster.reset();
+ }
+ }
+}