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 17package com.android.server; 18 19import android.os.Build; 20import android.os.Process; 21import android.util.Slog; 22 23import com.android.internal.util.ConcurrentUtils; 24import com.android.internal.util.Preconditions; 25 26import java.util.List; 27import java.util.concurrent.Callable; 28import java.util.concurrent.ExecutorService; 29import java.util.concurrent.Future; 30import java.util.concurrent.TimeUnit; 31 32/** 33 * Thread pool used during initialization of system server. 34 * <p>System services can {@link #submit(Runnable)} tasks for execution during boot. 35 * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}. 36 * New tasks <em>should not</em> be submitted afterwards. 37 * 38 * @hide 39 */ 40public class SystemServerInitThreadPool { 41 private static final String TAG = SystemServerInitThreadPool.class.getSimpleName(); 42 private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000; 43 private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE; 44 45 private static SystemServerInitThreadPool sInstance; 46 47 private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(4, 48 "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND); 49 50 public static synchronized SystemServerInitThreadPool get() { 51 if (sInstance == null) { 52 sInstance = new SystemServerInitThreadPool(); 53 } 54 Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG 55 + " - it has been shut down"); 56 return sInstance; 57 } 58 59 public Future<?> submit(Runnable runnable, String description) { 60 if (IS_DEBUGGABLE) { 61 return mService.submit(() -> { 62 Slog.d(TAG, "Started executing " + description); 63 try { 64 runnable.run(); 65 } catch (RuntimeException e) { 66 Slog.e(TAG, "Failure in " + description + ": " + e, e); 67 throw e; 68 } 69 Slog.d(TAG, "Finished executing " + description); 70 }); 71 } 72 return mService.submit(runnable); 73 } 74 75 static synchronized void shutdown() { 76 if (sInstance != null && sInstance.mService != null) { 77 sInstance.mService.shutdown(); 78 boolean terminated; 79 try { 80 terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS, 81 TimeUnit.MILLISECONDS); 82 } catch (InterruptedException e) { 83 Thread.currentThread().interrupt(); 84 throw new IllegalStateException(TAG + " init interrupted"); 85 } 86 List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow(); 87 if (!terminated) { 88 throw new IllegalStateException("Cannot shutdown. Unstarted tasks " 89 + unstartedRunnables); 90 } 91 sInstance.mService = null; // Make mService eligible for GC 92 Slog.d(TAG, "Shutdown successful"); 93 } 94 } 95 96} 97