1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server;
18 
19 import static android.os.Process.getThreadPriority;
20 import static android.os.Process.myTid;
21 import static android.os.Process.setThreadPriority;
22 
23 /**
24  * Utility class to boost threads in sections where important locks are held.
25  */
26 public class ThreadPriorityBooster {
27 
28     private static final boolean ENABLE_LOCK_GUARD = false;
29     private static final int PRIORITY_NOT_ADJUSTED = Integer.MAX_VALUE;
30 
31     private volatile int mBoostToPriority;
32     private final int mLockGuardIndex;
33 
34     private final ThreadLocal<PriorityState> mThreadState = new ThreadLocal<PriorityState>() {
35         @Override protected PriorityState initialValue() {
36             return new PriorityState();
37         }
38     };
39 
ThreadPriorityBooster(int boostToPriority, int lockGuardIndex)40     public ThreadPriorityBooster(int boostToPriority, int lockGuardIndex) {
41         mBoostToPriority = boostToPriority;
42         mLockGuardIndex = lockGuardIndex;
43     }
44 
boost()45     public void boost() {
46         final PriorityState state = mThreadState.get();
47         if (state.regionCounter == 0) {
48             final int prevPriority = getThreadPriority(state.tid);
49             if (prevPriority > mBoostToPriority) {
50                 setThreadPriority(state.tid, mBoostToPriority);
51                 state.prevPriority = prevPriority;
52             }
53         }
54         state.regionCounter++;
55         if (ENABLE_LOCK_GUARD) {
56             LockGuard.guard(mLockGuardIndex);
57         }
58     }
59 
reset()60     public void reset() {
61         final PriorityState state = mThreadState.get();
62         state.regionCounter--;
63         if (state.regionCounter == 0 && state.prevPriority != PRIORITY_NOT_ADJUSTED) {
64             setThreadPriority(state.tid, state.prevPriority);
65             state.prevPriority = PRIORITY_NOT_ADJUSTED;
66         }
67     }
68 
69     /**
70      * Updates the priority we boost the threads to, and updates the current thread's priority if
71      * necessary.
72      */
setBoostToPriority(int priority)73     protected void setBoostToPriority(int priority) {
74 
75         // We don't care about the other threads here, as long as they see the update of this
76         // variable immediately.
77         mBoostToPriority = priority;
78         final PriorityState state = mThreadState.get();
79         if (state.regionCounter != 0) {
80             final int prevPriority = getThreadPriority(state.tid);
81             if (prevPriority != priority) {
82                 setThreadPriority(state.tid, priority);
83             }
84         }
85     }
86 
87     private static class PriorityState {
88         final int tid = myTid();
89 
90         /**
91          * Acts as counter for number of synchronized region that needs to acquire 'this' as a lock
92          * the current thread is currently in. When it drops down to zero, we will no longer boost
93          * the thread's priority.
94          */
95         int regionCounter;
96 
97         /**
98          * The thread's previous priority before boosting.
99          */
100         int prevPriority = PRIORITY_NOT_ADJUSTED;
101     }
102 }
103