Added SystemServerInitThreadPool

System services can use it during the boot to submit tasks that can be run in
parallel with the main thread.

Switched PersistentDataBlockService and FingerprintService from FgThread to
the new thread pool.

UiModeManagerService: update initial configurations on init thread. They run
while holding the mLock so no extra synchronization barriers are needed at a
later stage.

Test: manual - device boots without errors
Test: ParallelPackageParserTest passes
Change-Id: I548f34b0a18f61924e09a39afb12e085cde35442
This commit is contained in:
Fyodor Kupolov
2016-12-16 16:14:17 -08:00
parent c9facc0a1f
commit e29a5a1152
7 changed files with 201 additions and 26 deletions

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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");
}
}
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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()) {