diff options
7 files changed, 201 insertions, 26 deletions
diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java new file mode 100644 index 000000000000..e35f9f45acfe --- /dev/null +++ b/core/java/com/android/internal/util/ConcurrentUtils.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 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.internal.util; + +import android.os.Process; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Utility methods for common functionality using java.util.concurrent package + * + * @hide + */ +public class ConcurrentUtils { + + private ConcurrentUtils() { + } + + /** + * Creates a thread pool using + * {@link java.util.concurrent.Executors#newFixedThreadPool(int, ThreadFactory)} + * + * @param nThreads the number of threads in the pool + * @param poolName base name of the threads in the pool + * @param linuxThreadPriority a Linux priority level. see {@link Process#setThreadPriority(int)} + * @return the newly created thread pool + */ + public static ExecutorService newFixedThreadPool(int nThreads, String poolName, + int linuxThreadPriority) { + return Executors.newFixedThreadPool(nThreads, + new ThreadFactory() { + private final AtomicInteger threadNum = new AtomicInteger(0); + + @Override + public Thread newThread(final Runnable r) { + return new Thread(poolName + threadNum.incrementAndGet()) { + @Override + public void run() { + Process.setThreadPriority(linuxThreadPriority); + r.run(); + } + }; + } + }); + } + + /** + * Waits if necessary for the computation to complete, and then retrieves its result. + * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread + * and throw {@code IllegalStateException}</p> + * + * @param future future to wait for result + * @param description short description of the operation + * @return the computed result + * @throws IllegalStateException if interrupted during wait + * @throws RuntimeException if an error occurs while waiting for {@link Future#get()} + * @see Future#get() + */ + public static <T> T waitForFutureNoInterrupt(Future<T> future, String description) { + try { + return future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(description + " interrupted"); + } catch (ExecutionException e) { + throw new RuntimeException(description + " failed", e); + } + } + +} diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java index 8a0d4df8ec27..417d375f302a 100644 --- a/services/core/java/com/android/server/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/PersistentDataBlockService.java @@ -116,12 +116,12 @@ public class PersistentDataBlockService extends SystemService { @Override public void onStart() { // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY - FgThread.getHandler().post(() -> { + SystemServerInitThreadPool.get().submit(() -> { enforceChecksumValidity(); formatIfOemUnlockEnabled(); publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); mInitDoneSignal.countDown(); - }); + }, TAG + ".onStart"); } @Override diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java new file mode 100644 index 000000000000..d1968507508a --- /dev/null +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 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 android.os.Build; +import android.os.Process; +import android.util.Slog; + +import com.android.internal.util.ConcurrentUtils; +import com.android.internal.util.Preconditions; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Thread pool used during initialization of system server. + * <p>System services can {@link #submit(Runnable)} tasks for execution during boot. + * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}. + * New tasks <em>should not</em> be submitted afterwards. + * + * @hide + */ +public class SystemServerInitThreadPool { + private static final String TAG = SystemServerInitThreadPool.class.getSimpleName(); + private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000; + private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE; + + private static SystemServerInitThreadPool sInstance; + + private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(2, + "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND); + + public static synchronized SystemServerInitThreadPool get() { + if (sInstance == null) { + sInstance = new SystemServerInitThreadPool(); + } + Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG + + " - it has been shut down"); + return sInstance; + } + + public Future<?> submit(Runnable runnable, String description) { + if (IS_DEBUGGABLE) { + return mService.submit(() -> { + Slog.d(TAG, "Started executing " + description); + try { + runnable.run(); + } catch (RuntimeException e) { + Slog.e(TAG, "Failure in " + description + ": " + e, e); + throw e; + } + Slog.d(TAG, "Finished executing " + description); + }); + } + return mService.submit(runnable); + } + + static synchronized void shutdown() { + if (sInstance != null && sInstance.mService != null) { + sInstance.mService.shutdown(); + boolean terminated; + try { + terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS, + TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(TAG + " init interrupted"); + } + List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow(); + if (!terminated) { + throw new IllegalStateException("Cannot shutdown. Unstarted tasks " + + unstartedRunnables); + } + sInstance.mService = null; // Make mService eligible for GC + Slog.d(TAG, "Shutdown successful"); + } + } + +} diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 6ea6fb7c9c03..5e1e1e0a5925 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -208,11 +208,13 @@ final class UiModeManagerService extends SystemService { Settings.Secure.UI_NIGHT_MODE, defaultNightMode); // Update the initial, static configurations. - synchronized (this) { - updateConfigurationLocked(); - sendConfigurationLocked(); - } + SystemServerInitThreadPool.get().submit(() -> { + synchronized (mLock) { + updateConfigurationLocked(); + sendConfigurationLocked(); + } + }, TAG + ".onStart"); publishBinderService(Context.UI_MODE_SERVICE, mService); } diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index 3f0ebf28bbfd..b0f67a81db28 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -54,7 +54,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; -import com.android.server.FgThread; +import com.android.server.SystemServerInitThreadPool; import com.android.server.SystemService; import org.json.JSONArray; @@ -1077,7 +1077,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe @Override public void onStart() { publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper()); - FgThread.getHandler().post(() -> getFingerprintDaemon()); + SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart"); listenForUserSwitches(); } diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java index 158cfc946b73..73125473df03 100644 --- a/services/core/java/com/android/server/pm/ParallelPackageParser.java +++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java @@ -22,15 +22,13 @@ import android.os.Trace; import android.util.DisplayMetrics; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ConcurrentUtils; import java.io.File; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; @@ -51,21 +49,8 @@ class ParallelPackageParser implements AutoCloseable { private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); - private final ExecutorService mService = Executors.newFixedThreadPool(MAX_THREADS, - new ThreadFactory() { - private final AtomicInteger threadNum = new AtomicInteger(0); - - @Override - public Thread newThread(final Runnable r) { - return new Thread("package-parsing-thread" + threadNum.incrementAndGet()) { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); - r.run(); - } - }; - } - }); + private final ExecutorService mService = ConcurrentUtils.newFixedThreadPool(MAX_THREADS, + "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND); ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps, DisplayMetrics metrics) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 1d550d294560..5b46f5144fd5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -345,6 +345,8 @@ public final class SystemServer { // Create the system service manager. mSystemServiceManager = new SystemServiceManager(mSystemContext); LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); + // Prepare the thread pool for init tasks that can be parallelized + SystemServerInitThreadPool.get(); } finally { traceEnd(); // InitBeforeStartServices } @@ -362,6 +364,7 @@ public final class SystemServer { } finally { traceEnd(); } + SystemServerInitThreadPool.shutdown(); // For debug builds, log event loop stalls to dropbox for analysis. if (StrictMode.conditionallyEnableDebugLogging()) { |