blob: 19f4089b07af2d15b95272648d6757bc21e2bf91 [file] [log] [blame]
Fyodor Kupolove29a5a12016-12-16 16:14:17 -08001/*
2 * Copyright (C) 2016 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
17package com.android.server;
18
Felipe Leme29f1ae92019-10-03 15:38:43 -070019import android.annotation.NonNull;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080020import android.os.Build;
21import android.os.Process;
22import android.util.Slog;
23
Felipe Leme29f1ae92019-10-03 15:38:43 -070024import com.android.internal.annotations.GuardedBy;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080025import com.android.internal.util.ConcurrentUtils;
26import com.android.internal.util.Preconditions;
Hui Yua4d089f2019-04-02 16:19:12 -070027import com.android.server.am.ActivityManagerService;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080028
Hui Yua4d089f2019-04-02 16:19:12 -070029import java.util.ArrayList;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080030import java.util.List;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080031import java.util.concurrent.ExecutorService;
32import java.util.concurrent.Future;
33import java.util.concurrent.TimeUnit;
34
35/**
36 * Thread pool used during initialization of system server.
Felipe Leme29f1ae92019-10-03 15:38:43 -070037 *
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080038 * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
39 * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
Felipe Leme29f1ae92019-10-03 15:38:43 -070040 *
41 * <p>New tasks <em>should not</em> be submitted afterwards.
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080042 *
43 * @hide
44 */
45public class SystemServerInitThreadPool {
46 private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
47 private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
48 private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
Felipe Leme29f1ae92019-10-03 15:38:43 -070049 private static final Object LOCK = new Object();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080050
Felipe Leme29f1ae92019-10-03 15:38:43 -070051 @GuardedBy("LOCK")
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080052 private static SystemServerInitThreadPool sInstance;
53
Felipe Leme29f1ae92019-10-03 15:38:43 -070054 private final ExecutorService mService;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080055
Felipe Leme29f1ae92019-10-03 15:38:43 -070056 @GuardedBy("mPendingTasks")
57 private final List<String> mPendingTasks = new ArrayList<>();
Hui Yua4d089f2019-04-02 16:19:12 -070058
Felipe Leme29f1ae92019-10-03 15:38:43 -070059 @GuardedBy("mPendingTasks")
60 private boolean mShutDown;
61
62 private SystemServerInitThreadPool() {
63 final int size = Runtime.getRuntime().availableProcessors();
64 Slog.i(TAG, "Creating instance with " + size + " threads");
65 mService = ConcurrentUtils.newFixedThreadPool(size,
66 "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080067 }
68
Felipe Leme29f1ae92019-10-03 15:38:43 -070069 /**
70 * Gets the singleton.
71 *
72 * @throws IllegalStateException if it hasn't been started or has been shut down already.
73 */
74 public static SystemServerInitThreadPool get() {
75 synchronized (LOCK) {
76 Preconditions.checkState(sInstance != null, "Cannot get " + TAG
77 + " - it has been shut down");
78 return sInstance;
79 }
80 }
81
82 /**
83 * Submits a task for execution.
84 */
85 public @NonNull Future<?> submit(@NonNull Runnable runnable, @NonNull String description) {
86 Preconditions.checkNotNull(description, "description cannot be null");
Hui Yua4d089f2019-04-02 16:19:12 -070087 synchronized (mPendingTasks) {
Felipe Leme29f1ae92019-10-03 15:38:43 -070088 Preconditions.checkState(!mShutDown, TAG + " already shut down");
Hui Yua4d089f2019-04-02 16:19:12 -070089 mPendingTasks.add(description);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080090 }
Hui Yua4d089f2019-04-02 16:19:12 -070091 return mService.submit(() -> {
92 if (IS_DEBUGGABLE) {
93 Slog.d(TAG, "Started executing " + description);
94 }
95 try {
96 runnable.run();
97 } catch (RuntimeException e) {
98 Slog.e(TAG, "Failure in " + description + ": " + e, e);
99 throw e;
100 }
101 synchronized (mPendingTasks) {
102 mPendingTasks.remove(description);
103 }
104 if (IS_DEBUGGABLE) {
105 Slog.d(TAG, "Finished executing " + description);
106 }
107 });
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800108 }
109
Felipe Leme29f1ae92019-10-03 15:38:43 -0700110 /**
111 * Starts it.
112 *
113 * <p>Note:</p> should only be called by {@link SystemServer}.
114 *
115 * @throws IllegalStateException if it has been started already without being shut down yet.
116 */
117 static void start() {
118 synchronized (LOCK) {
119 Preconditions.checkState(sInstance == null, TAG + " already started");
120 sInstance = new SystemServerInitThreadPool();
121 }
122 }
123
124 /**
125 * Shuts it down.
126 *
127 * <p>Note:</p> should only be called by {@link SystemServer}.
128 */
129 static void shutdown() {
130 synchronized (LOCK) {
131 if (sInstance == null) {
132 Slog.wtf(TAG, "Already shutdown", new Exception());
133 return;
134 }
135 synchronized (sInstance.mPendingTasks) {
136 sInstance.mShutDown = true;
137 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800138 sInstance.mService.shutdown();
Felipe Leme29f1ae92019-10-03 15:38:43 -0700139 final boolean terminated;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800140 try {
141 terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
142 TimeUnit.MILLISECONDS);
143 } catch (InterruptedException e) {
144 Thread.currentThread().interrupt();
Hui Yua4d089f2019-04-02 16:19:12 -0700145 dumpStackTraces();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800146 throw new IllegalStateException(TAG + " init interrupted");
147 }
Hui Yua4d089f2019-04-02 16:19:12 -0700148 if (!terminated) {
149 // dump stack must be called before shutdownNow() to collect stacktrace of threads
150 // in the thread pool.
151 dumpStackTraces();
152 }
Felipe Leme29f1ae92019-10-03 15:38:43 -0700153 final List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800154 if (!terminated) {
Hui Yua4d089f2019-04-02 16:19:12 -0700155 final List<String> copy = new ArrayList<>();
156 synchronized (sInstance.mPendingTasks) {
157 copy.addAll(sInstance.mPendingTasks);
158 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800159 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
Hui Yua4d089f2019-04-02 16:19:12 -0700160 + unstartedRunnables + " Unfinished tasks " + copy);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800161 }
Felipe Leme29f1ae92019-10-03 15:38:43 -0700162 sInstance = null; // Make eligible for GC
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800163 Slog.d(TAG, "Shutdown successful");
164 }
165 }
166
Hui Yua4d089f2019-04-02 16:19:12 -0700167 /**
168 * A helper function to call ActivityManagerService.dumpStackTraces().
169 */
170 private static void dumpStackTraces() {
171 final ArrayList<Integer> pids = new ArrayList<>();
172 pids.add(Process.myPid());
Hui Yu7e65c3f2019-04-16 17:03:16 -0700173 ActivityManagerService.dumpStackTraces(pids, null, null,
174 Watchdog.getInterestingNativePids());
Hui Yua4d089f2019-04-02 16:19:12 -0700175 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800176}