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