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

Commit fa8501d1 authored by Hans Boehm's avatar Hans Boehm
Browse files

Make setThreadPriority(int) cooperate with cache

The java.lang.Thread implementation caches Java priorities. Route
android.os.Process.setThreadPriority(int) requests through that cache.

This makes android.os.Process.setThreadPriority(int) compatible with
ART's temporary priority changes. In the future it may make similar,
less race-prone, temporary priority increases possible in other code.

This is important since we recommend setThreadPriority on thread start
(see https://developer.android.com/topic/performance/threads#priority).
It should not handicap ART performance.

(If we could design these APIs from scratch, there is much I would
change, but ...)

Improve the Process documentation by documenting existing important
behavior that surprised me. Nothing here is inconsistent with the
old spec, or changes anything other than performance.

Update some other documentation to reflect the new reality.
ThreadPriorityBooster may be worth future re-examination.

Have all versions of setThreadPriority throw an
IllegalArgumentException for priority arguments outside [-20, 19].

Bug: 417492941
Flag: com.android.libcore.niceness_apis
Test: atest ProcessTest, choosing the modified test
Change-Id: I46fea0357a843d9f64f93eb6880951c4c9432d54
parent 1b105a76
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2613,6 +2613,9 @@ public class Instrumentation {
        }
        public void run() {
            try {
                // We have historically always done this in a way that does not propagate to
                // Java-created child threads. It is unclear whether this is really necessary
                // or useful, but it is less risky than a change.
                Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
            } catch (RuntimeException e) {
                Log.w(TAG, "Exception setting priority of instrumentation thread "
+57 −15
Original line number Diff line number Diff line
@@ -1083,20 +1083,41 @@ public class Process {
    }

    /**
     * Set the priority of a thread, based on Linux priorities.
     * Set the OS priority of a thread, using Linux niceness priorities. Does not affect the value
     * cached for use by {@code java.lang.Thread.getPriority()}. If this is used with a
     * non-negative priority (Linux niceness), the priority may, on rare occasion, be reset
     * by the runtime to its cached value, especially when setting the priority of another thread.
     *
     * The new priority is not inherited by Java-created child threads. It may or may not
     * be inherited by threads created from native code. Use {@code
     * java/lang/Thread.setPriority()} to allow child threads to inherit the new priority.
     *
     * @param tid The identifier of the thread/process to change.
     * @param priority A Linux priority level, from -20 for highest scheduling
     * @param priority A Linux priority a.k.a. "niceness" level, from -20 for highest scheduling
     * priority to 19 for lowest scheduling priority.
     *
     * @throws IllegalArgumentException Throws IllegalArgumentException if
     * <var>tid</var> does not exist.
     * <var>tid</var> does not exist, or <var>priority</var> is out of range.
     * @throws SecurityException Throws SecurityException if your process does
     * not have permission to modify the given thread, or to use the given
     * priority.
     */
    @RavenwoodRedirect
    public static final native void setThreadPriority(int tid,
    public static final void setThreadPriority(int tid,
            @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority)
            throws IllegalArgumentException, SecurityException {
        if (com.android.libcore.Flags.nicenessApis() && Process.myTid() == tid) {
            // Prefer the same thread version that informs ART of the priority change.
            setThreadPriority(priority);
        } else {
            if (priority < -20 || priority > THREAD_PRIORITY_LOWEST) {
                throw new IllegalArgumentException("Priority/niceness " + priority + " is invalid");
            }
            setThreadPriorityNative(tid, priority);
        }
    }

    private static native void setThreadPriorityNative(int tid,
            @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority)
            throws IllegalArgumentException, SecurityException;

@@ -1104,12 +1125,17 @@ public class Process {
     * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
     * throw an exception if passed a background-level thread priority.  This is only
     * effective if the JNI layer is built with GUARD_THREAD_PRIORITY defined to 1.
     * This does not prevent a thread from backgrounding itself via other means, such
     * as a call to Thread.setPriority() or a native setpriority() call.
     *
     * @hide
     */
    @RavenwoodRedirect
    public static final native void setCanSelfBackground(boolean backgroundOk);

    @RavenwoodRedirect
    private static native boolean getCanSelfBackground();

    /**
     * Sets the scheduling group for a thread.
     * @hide
@@ -1249,31 +1275,47 @@ public class Process {
    public static final native long[] getSchedAffinity(int tid);

    /**
     * Set the priority of the calling thread, based on Linux priorities.  See
     * {@link #setThreadPriority(int, int)} for more information.
     * Set the priority of the calling thread, based on Linux niceness priorities.  See
     * {@link #setThreadPriority(int, int)} for more information. This is preferred over
     * the two argument version when possible. The new priority is not inherited by Java
     * child threads.
     *
     * @param priority A Linux priority level, from -20 for highest scheduling
     * priority to 19 for lowest scheduling priority.
     *
     * @throws IllegalArgumentException Throws IllegalArgumentException if
     * <var>tid</var> does not exist.
     * <var>priority</var> is out of range.
     * @throws SecurityException Throws SecurityException if your process does
     * not have permission to modify the given thread, or to use the given
     * priority.
     *
     * @see #setThreadPriority(int, int)
     */
    @RavenwoodReplace
    public static final native void setThreadPriority(
    @RavenwoodRedirect
    public static final void setThreadPriority(
            @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority)
            throws IllegalArgumentException, SecurityException;

    private static void setThreadPriority$ravenwood(int priority) {
            throws IllegalArgumentException, SecurityException {
        if (!com.android.libcore.Flags.nicenessApis()) {
            // Fall back to not updating the cached priority if we don't have libcore support.
            setThreadPriority(myTid(), priority);
            return;
        }
        if (priority >= THREAD_PRIORITY_BACKGROUND && !getCanSelfBackground()) {
            throw new IllegalArgumentException(
                "Priority " + priority + " blocked by setCanSelfBackground()");
        }
        boolean succ = VMRuntime.getRuntime().setThreadNiceness(Thread.currentThread(), priority);
        // VMRuntime.setThreadNiceness() just returns false for out-of-range priority.
        if (!succ) {
            if (priority < -20 || priority > THREAD_PRIORITY_LOWEST) {
                throw new IllegalArgumentException("Priority/niceness " + priority + " is invalid");
            }
            throw new SecurityException("Cannot set priority to " + priority);
        }
    }

    /**
     * Return the current priority of a thread, based on Linux priorities.
     * Ignores the {@code java.lang.Thread.getPriority()} cached priority, which is used
     * to set the priority of newly created child Java threads.
     *
     * @param tid The identifier of the thread/process. If tid equals zero, the priority of the
     * calling process/thread will be returned.
+24 −6
Original line number Diff line number Diff line
@@ -36,16 +36,27 @@ public class Process_ravenwood {
     */
    public static void setThreadPriority(int tid, int priority) {
        if (Process.myTid() == tid) {
            boolean backgroundOk = sThreadPriority.get().second;
            setThreadPriority(priority);
        } else {
            throw new UnsupportedOperationException(
                    "Cross-thread priority management not yet available in Ravenwood");
        }
    }

    /**
     * Called by {@link Process#setThreadPriority(int)}
     * The real implementation uses an Android-specific API.
     */
    public static void setThreadPriority(int priority) {
        boolean backgroundOk = getCanSelfBackground();
        if (priority >= Process.THREAD_PRIORITY_BACKGROUND && !backgroundOk) {
            throw new IllegalArgumentException(
                    "Priority " + priority + " blocked by setCanSelfBackground()");
        }
            sThreadPriority.set(Pair.create(priority, backgroundOk));
        } else {
            throw new UnsupportedOperationException(
                    "Cross-thread priority management not yet available in Ravenwood");
        if (priority < -20 || priority > 19) {
            throw new IllegalArgumentException("Priority/niceness " + priority + " is invalid");
        }
        sThreadPriority.set(Pair.create(priority, backgroundOk));
    }

    /**
@@ -56,6 +67,13 @@ public class Process_ravenwood {
        sThreadPriority.set(Pair.create(priority, backgroundOk));
    }

    /**
     * Called by {@link Process#getCanSelfBackground(int)}
     */
    public static boolean getCanSelfBackground() {
        return sThreadPriority.get().second;
    }

    /**
     * Called by {@link Process#getThreadPriority(int)}
     */
+14 −12
Original line number Diff line number Diff line
@@ -540,7 +540,7 @@ static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz,
#if GUARD_THREAD_PRIORITY
    ALOGV("Process.setCanSelfBackground(%d) : tid=%d", bgOk, gettid());
    {
        Mutex::Autolock _l(gKeyCreateMutex);
        Mutex::Autolock _l(gKeyCreateMutex); // Acquired nowhere else.
        if (gBgKey == -1) {
            pthread_key_create(&gBgKey, NULL);
        }
@@ -551,6 +551,16 @@ static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz,
#endif
}

static jboolean android_os_Process_getCanSelfBackground(JNIEnv* env, jclass clazz) {
#if GUARD_THREAD_PRIORITY
    void* bgOk = pthread_getspecific(gBgKey);
    if (bgOk == ((void*)0xbaad)) {
        return false;
    }
#endif
    return true;
}

jint android_os_Process_getThreadScheduler(JNIEnv* env, jclass clazz,
                                              jint tid)
{
@@ -584,9 +594,7 @@ void android_os_Process_setThreadScheduler(JNIEnv* env, jclass clazz,
#endif
}

void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                              jint pid, jint pri)
{
void android_os_Process_setThreadPriorityNative(JNIEnv* env, jobject clazz, jint pid, jint pri) {
#if GUARD_THREAD_PRIORITY
    // if we're putting the current thread into the background, check the TLS
    // to make sure this thread isn't guarded.  If it is, raise an exception.
@@ -615,12 +623,6 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
    //     pid, pri, getpriority(PRIO_PROCESS, pid));
}

void android_os_Process_setCallingThreadPriority(JNIEnv* env, jobject clazz,
                                                        jint pri)
{
    android_os_Process_setThreadPriority(env, clazz, gettid(), pri);
}

jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz,
                                              jint pid)
{
@@ -1406,10 +1408,10 @@ void android_os_Process_freezeCgroupUID(JNIEnv* env, jobject clazz, jint uid, jb
static const JNINativeMethod methods[] = {
        {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
        {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
        {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority},
        {"setThreadPriorityNative", "(II)V", (void*)android_os_Process_setThreadPriorityNative},
        {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler},
        {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
        {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority},
        {"getCanSelfBackground", "()Z", (void*)android_os_Process_getCanSelfBackground},
        {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority},
        {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler},
        {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
+2 −0
Original line number Diff line number Diff line
@@ -106,6 +106,8 @@ public class BinderThreadPriorityTest {
    public void tearDown() throws Exception {
        // HACK -- see bug 2665914 -- setThreadPriority() doesn't always set the
        // scheduler group reliably unless we start out with background priority.
        // As of July, 2025 this looks obsolete and incorrect, probably making the
        // hack unnecessary, but not seriously harmful.
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Process.setThreadPriority(mSavedPriority);
        assertEquals(mSavedPriority, Process.getThreadPriority(Process.myTid()));
Loading