blob: 8f914fe6f59fa48e3961a34e24c29afa4fa73422 [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;
Daulet Zhanguzinea1a7ca2020-01-03 09:46:50 +000031import java.util.Objects;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080032import java.util.concurrent.ExecutorService;
33import java.util.concurrent.Future;
34import java.util.concurrent.TimeUnit;
35
36/**
37 * Thread pool used during initialization of system server.
Felipe Leme29f1ae92019-10-03 15:38:43 -070038 *
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080039 * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
40 * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
Felipe Leme29f1ae92019-10-03 15:38:43 -070041 *
42 * <p>New tasks <em>should not</em> be submitted afterwards.
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080043 *
44 * @hide
45 */
46public class SystemServerInitThreadPool {
47 private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
48 private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
49 private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
Felipe Leme29f1ae92019-10-03 15:38:43 -070050 private static final Object LOCK = new Object();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080051
Felipe Leme29f1ae92019-10-03 15:38:43 -070052 @GuardedBy("LOCK")
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080053 private static SystemServerInitThreadPool sInstance;
54
Felipe Leme29f1ae92019-10-03 15:38:43 -070055 private final ExecutorService mService;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080056
Felipe Leme29f1ae92019-10-03 15:38:43 -070057 @GuardedBy("mPendingTasks")
58 private final List<String> mPendingTasks = new ArrayList<>();
Hui Yua4d089f2019-04-02 16:19:12 -070059
Felipe Leme29f1ae92019-10-03 15:38:43 -070060 @GuardedBy("mPendingTasks")
61 private boolean mShutDown;
62
63 private SystemServerInitThreadPool() {
64 final int size = Runtime.getRuntime().availableProcessors();
65 Slog.i(TAG, "Creating instance with " + size + " threads");
66 mService = ConcurrentUtils.newFixedThreadPool(size,
67 "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080068 }
69
Felipe Leme29f1ae92019-10-03 15:38:43 -070070 /**
Felipe Lemeb68b7692019-10-09 10:43:03 -070071 * Submits a task for execution.
Felipe Leme29f1ae92019-10-03 15:38:43 -070072 *
73 * @throws IllegalStateException if it hasn't been started or has been shut down already.
74 */
Felipe Lemeb68b7692019-10-09 10:43:03 -070075 public static @NonNull Future<?> submit(@NonNull Runnable runnable,
76 @NonNull String description) {
Daulet Zhanguzinea1a7ca2020-01-03 09:46:50 +000077 Objects.requireNonNull(description, "description cannot be null");
Felipe Lemeb68b7692019-10-09 10:43:03 -070078
79 SystemServerInitThreadPool instance;
Felipe Leme29f1ae92019-10-03 15:38:43 -070080 synchronized (LOCK) {
81 Preconditions.checkState(sInstance != null, "Cannot get " + TAG
82 + " - it has been shut down");
Felipe Lemeb68b7692019-10-09 10:43:03 -070083 instance = sInstance;
Felipe Leme29f1ae92019-10-03 15:38:43 -070084 }
Felipe Lemeb68b7692019-10-09 10:43:03 -070085
86 return instance.submitTask(runnable, description);
Felipe Leme29f1ae92019-10-03 15:38:43 -070087 }
88
Felipe Lemeb68b7692019-10-09 10:43:03 -070089 private @NonNull Future<?> submitTask(@NonNull Runnable runnable,
90 @NonNull String description) {
Hui Yua4d089f2019-04-02 16:19:12 -070091 synchronized (mPendingTasks) {
Felipe Leme29f1ae92019-10-03 15:38:43 -070092 Preconditions.checkState(!mShutDown, TAG + " already shut down");
Hui Yua4d089f2019-04-02 16:19:12 -070093 mPendingTasks.add(description);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -080094 }
Hui Yua4d089f2019-04-02 16:19:12 -070095 return mService.submit(() -> {
96 if (IS_DEBUGGABLE) {
97 Slog.d(TAG, "Started executing " + description);
98 }
99 try {
100 runnable.run();
101 } catch (RuntimeException e) {
102 Slog.e(TAG, "Failure in " + description + ": " + e, e);
103 throw e;
104 }
105 synchronized (mPendingTasks) {
106 mPendingTasks.remove(description);
107 }
108 if (IS_DEBUGGABLE) {
109 Slog.d(TAG, "Finished executing " + description);
110 }
111 });
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800112 }
113
Felipe Leme29f1ae92019-10-03 15:38:43 -0700114 /**
115 * Starts it.
116 *
117 * <p>Note:</p> should only be called by {@link SystemServer}.
118 *
119 * @throws IllegalStateException if it has been started already without being shut down yet.
120 */
121 static void start() {
122 synchronized (LOCK) {
123 Preconditions.checkState(sInstance == null, TAG + " already started");
124 sInstance = new SystemServerInitThreadPool();
125 }
126 }
127
128 /**
129 * Shuts it down.
130 *
131 * <p>Note:</p> should only be called by {@link SystemServer}.
132 */
133 static void shutdown() {
134 synchronized (LOCK) {
135 if (sInstance == null) {
136 Slog.wtf(TAG, "Already shutdown", new Exception());
137 return;
138 }
139 synchronized (sInstance.mPendingTasks) {
140 sInstance.mShutDown = true;
141 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800142 sInstance.mService.shutdown();
Felipe Leme29f1ae92019-10-03 15:38:43 -0700143 final boolean terminated;
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800144 try {
145 terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
146 TimeUnit.MILLISECONDS);
147 } catch (InterruptedException e) {
148 Thread.currentThread().interrupt();
Hui Yua4d089f2019-04-02 16:19:12 -0700149 dumpStackTraces();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800150 throw new IllegalStateException(TAG + " init interrupted");
151 }
Hui Yua4d089f2019-04-02 16:19:12 -0700152 if (!terminated) {
153 // dump stack must be called before shutdownNow() to collect stacktrace of threads
154 // in the thread pool.
155 dumpStackTraces();
156 }
Felipe Leme29f1ae92019-10-03 15:38:43 -0700157 final List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800158 if (!terminated) {
Hui Yua4d089f2019-04-02 16:19:12 -0700159 final List<String> copy = new ArrayList<>();
160 synchronized (sInstance.mPendingTasks) {
161 copy.addAll(sInstance.mPendingTasks);
162 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800163 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
Hui Yua4d089f2019-04-02 16:19:12 -0700164 + unstartedRunnables + " Unfinished tasks " + copy);
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800165 }
Felipe Leme29f1ae92019-10-03 15:38:43 -0700166 sInstance = null; // Make eligible for GC
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800167 Slog.d(TAG, "Shutdown successful");
168 }
169 }
170
Hui Yua4d089f2019-04-02 16:19:12 -0700171 /**
172 * A helper function to call ActivityManagerService.dumpStackTraces().
173 */
174 private static void dumpStackTraces() {
175 final ArrayList<Integer> pids = new ArrayList<>();
176 pids.add(Process.myPid());
Hui Yu7e65c3f2019-04-16 17:03:16 -0700177 ActivityManagerService.dumpStackTraces(pids, null, null,
Simon MacMullen46755e22020-02-04 14:03:37 +0000178 Watchdog.getInterestingNativePids(), null);
Hui Yua4d089f2019-04-02 16:19:12 -0700179 }
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800180}