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

Commit 2bbd28e4 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Avoid locks in various singleton HandlerThread types" into main

parents c808b228 198ebf4b
Loading
Loading
Loading
Loading
+34 −28
Original line number Diff line number Diff line
@@ -32,9 +32,13 @@ import java.util.concurrent.Executor;
public final class BackgroundThread extends HandlerThread {
    private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
    private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
    private static BackgroundThread sInstance;
    private static Handler sHandler;
    private static HandlerExecutor sHandlerExecutor;

    // Note: These static fields are shadowed in Robolectric, and cannot be easily changed without
    // breaking downstream tests. This makes refactoring or optimization a bit messier than it could
    // be, e.g., using common lazy singleton abstractions or the holder init pattern.
    private static volatile BackgroundThread sInstance;
    private static volatile Handler sHandler;
    private static volatile HandlerExecutor sHandlerExecutor;

    private BackgroundThread() {
        super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -47,9 +51,14 @@ public final class BackgroundThread extends HandlerThread {
        }
    }

    private static void ensureThreadReadyLocked() {
    private static void ensureThreadReady() {
        // As the executor is assigned and initialized last, we use it to signal full readiness.
        if (sHandlerExecutor != null) {
            return;
        }
        synchronized (BackgroundThread.class) {
            if (sHandlerExecutor == null) {
                ensureThreadStartedLocked();
        if (sHandler == null) {
                // This will block until the looper is initialized on the background thread.
                final Looper looper = sInstance.getLooper();
                looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -60,37 +69,34 @@ public final class BackgroundThread extends HandlerThread {
                sHandlerExecutor = new HandlerExecutor(sHandler);
            }
        }
    }

    /**
     * Starts the thread if needed, but doesn't block on thread initialization or readiness.
     */
    public static void startIfNeeded() {
        if (sInstance == null) {
            synchronized (BackgroundThread.class) {
                ensureThreadStartedLocked();
            }
        }
    }

    @NonNull
    public static BackgroundThread get() {
        synchronized (BackgroundThread.class) {
            ensureThreadReadyLocked();
        ensureThreadReady();
        return sInstance;
    }
    }

    @NonNull
    public static Handler getHandler() {
        synchronized (BackgroundThread.class) {
            ensureThreadReadyLocked();
        ensureThreadReady();
        return sHandler;
    }
    }

    @NonNull
    public static Executor getExecutor() {
        synchronized (BackgroundThread.class) {
            ensureThreadReadyLocked();
        ensureThreadReady();
        return sHandlerExecutor;
    }
}
}
+15 −28
Original line number Diff line number Diff line
@@ -38,45 +38,32 @@ public final class FgThread extends ServiceThread {
    private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
    private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;

    private static FgThread sInstance;
    private static Handler sHandler;
    private static HandlerExecutor sHandlerExecutor;
    private static final class NoPreloadHolder {
        private static final FgThread sInstance = new FgThread();
    }

    private final Handler mHandler;
    private final HandlerExecutor mHandlerExecutor;

    private FgThread() {
        super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
    }

    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new FgThread();
            sInstance.start();
            final Looper looper = sInstance.getLooper();
        start();
        final Looper looper = getLooper();
        looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
            looper.setSlowLogThresholdMs(
                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
            sHandler = makeSharedHandler(sInstance.getLooper());
            sHandlerExecutor = new HandlerExecutor(sHandler);
        }
        looper.setSlowLogThresholdMs(SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
        mHandler = makeSharedHandler(looper);
        mHandlerExecutor = new HandlerExecutor(mHandler);
    }

    public static FgThread get() {
        synchronized (FgThread.class) {
            ensureThreadLocked();
            return sInstance;
        }
        return NoPreloadHolder.sInstance;
    }

    public static Handler getHandler() {
        synchronized (FgThread.class) {
            ensureThreadLocked();
            return sHandler;
        }
        return NoPreloadHolder.sInstance.mHandler;
    }

    public static Executor getExecutor() {
        synchronized (FgThread.class) {
            ensureThreadLocked();
            return sHandlerExecutor;
        }
        return NoPreloadHolder.sInstance.mHandlerExecutor;
    }
}
+15 −25
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server;

import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Trace;

import java.util.concurrent.Executor;
@@ -28,42 +29,31 @@ import java.util.concurrent.Executor;
 * (not waiting for data itself, but communicating with network daemons).
 */
public final class IoThread extends ServiceThread {
    private static IoThread sInstance;
    private static Handler sHandler;
    private static HandlerExecutor sHandlerExecutor;
    private static final class NoPreloadHolder {
        private static final IoThread sInstance = new IoThread();
    }

    private final Handler mHandler;
    private final HandlerExecutor mHandlerExecutor;

    private IoThread() {
        super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
    }

    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new IoThread();
            sInstance.start();
            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
            sHandler = makeSharedHandler(sInstance.getLooper());
            sHandlerExecutor = new HandlerExecutor(sHandler);
        }
        start();
        final Looper looper = getLooper();
        looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
        mHandler = new Handler(looper, /*callback=*/ null, /* async=*/ false, /* shared=*/ true);
        mHandlerExecutor = new HandlerExecutor(mHandler);
    }

    public static IoThread get() {
        synchronized (IoThread.class) {
            ensureThreadLocked();
            return sInstance;
        }
        return NoPreloadHolder.sInstance;
    }

    public static Handler getHandler() {
        synchronized (IoThread.class) {
            ensureThreadLocked();
            return sHandler;
        }
        return NoPreloadHolder.sInstance.mHandler;
    }

    public static Executor getExecutor() {
        synchronized (IoThread.class) {
            ensureThreadLocked();
            return sHandlerExecutor;
        }
        return NoPreloadHolder.sInstance.mHandlerExecutor;
    }
}
+13 −35
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Trace;

import com.android.internal.annotations.GuardedBy;

import java.util.concurrent.Executor;

/**
@@ -34,61 +32,41 @@ public final class PermissionThread extends ServiceThread {
    private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
    private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;

    private static final Object sLock = new Object();
    private static final class NoPreloadHolder {
        private static final PermissionThread sInstance = new PermissionThread();
    }

    @GuardedBy("sLock")
    private static PermissionThread sInstance;
    private static Handler sHandler;
    private static HandlerExecutor sHandlerExecutor;
    private final Handler mHandler;
    private final HandlerExecutor mHandlerExecutor;

    private PermissionThread() {
        super("android.perm", android.os.Process.THREAD_PRIORITY_DEFAULT, /* allowIo= */ true);
    }

    @GuardedBy("sLock")
    private static void ensureThreadLocked() {
        if (sInstance != null) {
            return;
        }

        sInstance = new PermissionThread();
        sInstance.start();
        final Looper looper = sInstance.getLooper();
        start();
        final Looper looper = getLooper();
        looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
        looper.setSlowLogThresholdMs(
                SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
        sHandler = new Handler(sInstance.getLooper());
        sHandlerExecutor = new HandlerExecutor(sHandler);
        looper.setSlowLogThresholdMs(SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
        mHandler = new Handler(looper);
        mHandlerExecutor = new HandlerExecutor(mHandler);
    }

    /**
     * Obtain a singleton instance of the PermissionThread.
     */
    public static PermissionThread get() {
        synchronized (sLock) {
            ensureThreadLocked();
            return sInstance;
        }
        return NoPreloadHolder.sInstance;
    }

    /**
     * Obtain a singleton instance of a handler executing in the PermissionThread.
     */
    public static Handler getHandler() {
        synchronized (sLock) {
            ensureThreadLocked();
            return sHandler;
        }
        return NoPreloadHolder.sInstance.mHandler;
    }


    /**
     * Obtain a singleton instance of an executor of the PermissionThread.
     */
    public static Executor getExecutor() {
        synchronized (sLock) {
            ensureThreadLocked();
            return sHandlerExecutor;
        }
        return NoPreloadHolder.sInstance.mHandlerExecutor;
    }
}