1 /*
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 
17 package com.android.server;
18 
19 import android.os.Build;
20 import android.os.Process;
21 import android.util.Slog;
22 
23 import com.android.internal.util.ConcurrentUtils;
24 import com.android.internal.util.Preconditions;
25 import com.android.server.am.ActivityManagerService;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Objects;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 
34 /**
35  * Thread pool used during initialization of system server.
36  * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
37  * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
38  * New tasks <em>should not</em> be submitted afterwards.
39  *
40  * @hide
41  */
42 public class SystemServerInitThreadPool {
43     private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
44     private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
45     private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
46 
47     private static SystemServerInitThreadPool sInstance;
48 
49     private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(
50             Runtime.getRuntime().availableProcessors(),
51             "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
52 
53     private List<String> mPendingTasks = new ArrayList<>();
54 
get()55     public static synchronized SystemServerInitThreadPool get() {
56         if (sInstance == null) {
57             sInstance = new SystemServerInitThreadPool();
58         }
59         Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG
60                 + " - it has been shut down");
61         return sInstance;
62     }
63 
submit(Runnable runnable, String description)64     public Future<?> submit(Runnable runnable, String description) {
65         synchronized (mPendingTasks) {
66             mPendingTasks.add(description);
67         }
68         return mService.submit(() -> {
69             if (IS_DEBUGGABLE) {
70                 Slog.d(TAG, "Started executing " + description);
71             }
72             try {
73                 runnable.run();
74             } catch (RuntimeException e) {
75                 Slog.e(TAG, "Failure in " + description + ": " + e, e);
76                 throw e;
77             }
78             synchronized (mPendingTasks) {
79                 mPendingTasks.remove(description);
80             }
81             if (IS_DEBUGGABLE) {
82                 Slog.d(TAG, "Finished executing " + description);
83             }
84         });
85     }
86 
87     static synchronized void shutdown() {
88         if (sInstance != null && sInstance.mService != null) {
89             sInstance.mService.shutdown();
90             boolean terminated;
91             try {
92                 terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
93                         TimeUnit.MILLISECONDS);
94             } catch (InterruptedException e) {
95                 Thread.currentThread().interrupt();
96                 dumpStackTraces();
97                 throw new IllegalStateException(TAG + " init interrupted");
98             }
99             if (!terminated) {
100                 // dump stack must be called before shutdownNow() to collect stacktrace of threads
101                 // in the thread pool.
102                 dumpStackTraces();
103             }
104             List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
105             if (!terminated) {
106                 final List<String> copy = new ArrayList<>();
107                 synchronized (sInstance.mPendingTasks) {
108                     copy.addAll(sInstance.mPendingTasks);
109                 }
110                 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
111                         + unstartedRunnables + " Unfinished tasks " + copy);
112             }
113             sInstance.mService = null; // Make mService eligible for GC
114             sInstance.mPendingTasks = null;
115             Slog.d(TAG, "Shutdown successful");
116         }
117     }
118 
119     /**
120      * A helper function to call ActivityManagerService.dumpStackTraces().
121      */
122     private static void dumpStackTraces() {
123         final ArrayList<Integer> pids = new ArrayList<>();
124         pids.add(Process.myPid());
125         ActivityManagerService.dumpStackTraces(pids, null, null,
126                 Watchdog.getInterestingNativePids());
127     }
128 }
129