1e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov/*
2e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * Copyright (C) 2016 The Android Open Source Project
3e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov *
4e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * Licensed under the Apache License, Version 2.0 (the "License");
5e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * you may not use this file except in compliance with the License.
6e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * You may obtain a copy of the License at
7e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov *
8e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov *      http://www.apache.org/licenses/LICENSE-2.0
9e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov *
10e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * Unless required by applicable law or agreed to in writing, software
11e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * distributed under the License is distributed on an "AS IS" BASIS,
12e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * See the License for the specific language governing permissions and
14e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * limitations under the License
15e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov */
16e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
17e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovpackage com.android.server;
18e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
19e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport android.os.Build;
20e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport android.os.Process;
21e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport android.util.Slog;
22e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
23e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport com.android.internal.util.ConcurrentUtils;
24e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport com.android.internal.util.Preconditions;
25e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
26e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport java.util.List;
27e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport java.util.concurrent.Callable;
28e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport java.util.concurrent.ExecutorService;
29e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport java.util.concurrent.Future;
30e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovimport java.util.concurrent.TimeUnit;
31e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
32e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov/**
33e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * Thread pool used during initialization of system server.
34e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
35e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
36e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * New tasks <em>should not</em> be submitted afterwards.
37e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov *
38e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov * @hide
39e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov */
40e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolovpublic class SystemServerInitThreadPool {
41e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
42e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
43e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
44e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
45e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    private static SystemServerInitThreadPool sInstance;
46e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
47b76e0fdf1ef2767e2d4f0ced2886756ff9556b19Fyodor Kupolov    private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(4,
48e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
49e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
50e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    public static synchronized SystemServerInitThreadPool get() {
51e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        if (sInstance == null) {
52e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            sInstance = new SystemServerInitThreadPool();
53e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        }
54e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG
55e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                + " - it has been shut down");
56e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        return sInstance;
57e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    }
58e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
59e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    public Future<?> submit(Runnable runnable, String description) {
60e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        if (IS_DEBUGGABLE) {
61e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            return mService.submit(() -> {
62e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                Slog.d(TAG, "Started executing " + description);
63e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                try {
64e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                    runnable.run();
65e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                } catch (RuntimeException e) {
66e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                    Slog.e(TAG, "Failure in " + description + ": " + e, e);
67e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                    throw e;
68e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                }
69e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                Slog.d(TAG, "Finished executing "  + description);
70e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            });
71e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        }
72e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        return mService.submit(runnable);
73e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    }
74e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
75e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    static synchronized void shutdown() {
76e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        if (sInstance != null && sInstance.mService != null) {
77e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            sInstance.mService.shutdown();
78e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            boolean terminated;
79e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            try {
80e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
81e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                        TimeUnit.MILLISECONDS);
82e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            } catch (InterruptedException e) {
83e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                Thread.currentThread().interrupt();
84e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                throw new IllegalStateException(TAG + " init interrupted");
85e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            }
86e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
87e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            if (!terminated) {
88e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
89e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov                        + unstartedRunnables);
90e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            }
91e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            sInstance.mService = null; // Make mService eligible for GC
92e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov            Slog.d(TAG, "Shutdown successful");
93e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov        }
94e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov    }
95e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov
96e29a5a11529dc7df82911b48b9f95461383cbcc2Fyodor Kupolov}
97