1 /*
2  * Copyright (C) 2021 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.car.internal.util;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.car.builtin.util.Slogf;
22 import android.os.Process;
23 
24 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
25 
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.Executor;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.ThreadFactory;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicInteger;
35 
36 // Copied from frameworks/base
37 /**
38  * Utility methods for common functionality using java.util.concurrent package
39  *
40  * @hide
41  */
42 public final class ConcurrentUtils {
43 
ConcurrentUtils()44     private ConcurrentUtils() {
45     }
46 
47     public static final Executor DIRECT_EXECUTOR = new DirectExecutor();
48 
49     /**
50      * Creates a thread pool using
51      * {@link java.util.concurrent.Executors#newFixedThreadPool(int, ThreadFactory)}
52      *
53      * @param nThreads the number of threads in the pool
54      * @param poolName base name of the threads in the pool
55      * @param linuxThreadPriority a Linux priority level. see {@link Process#setThreadPriority(int)}
56      * @return the newly created thread pool
57      */
newFixedThreadPool(int nThreads, String poolName, int linuxThreadPriority)58     public static ExecutorService newFixedThreadPool(int nThreads, String poolName,
59             int linuxThreadPriority) {
60         return Executors.newFixedThreadPool(nThreads,
61                 new ThreadFactory() {
62                     private final AtomicInteger mThreadNum = new AtomicInteger(0);
63 
64                     @Override
65                     public Thread newThread(final Runnable r) {
66                         return new Thread(poolName + mThreadNum.incrementAndGet()) {
67                             @Override
68                             public void run() {
69                                 Process.setThreadPriority(linuxThreadPriority);
70                                 r.run();
71                             }
72                         };
73                     }
74                 });
75     }
76 
77     /**
78      * Waits if necessary for the computation to complete, and then retrieves its result.
79      * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
80      * and throw {@code IllegalStateException}</p>
81      *
82      * @param future future to wait for result
83      * @param description short description of the operation
84      * @return the computed result
85      * @throws IllegalStateException if interrupted during wait
86      * @throws RuntimeException if an error occurs while waiting for {@link Future#get()}
87      * @see Future#get()
88      */
89     public static <T> T waitForFutureNoInterrupt(Future<T> future, String description) {
90         try {
91             return future.get();
92         } catch (InterruptedException e) {
93             Thread.currentThread().interrupt();
94             throw new IllegalStateException(description + " interrupted", e);
95         } catch (ExecutionException e) {
96             throw new RuntimeException(description + " failed", e);
97         }
98     }
99 
100     /**
101      * Waits for {@link CountDownLatch#countDown()} to be called on the {@code countDownLatch}.
102      * <p>If {@link CountDownLatch#countDown()} doesn't occur within {@code timeoutMs}, this
103      * method will throw {@code IllegalStateException}
104      * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
105      * and throw {@code IllegalStateException}
106      *
107      * @param countDownLatch the CountDownLatch which {@link CountDownLatch#countDown()} is
108      *                       being waited on.
109      * @param timeoutMs the maximum time waited for {@link CountDownLatch#countDown()}
110      * @param description a short description of the operation
111      */
112     public static void waitForCountDownNoInterrupt(CountDownLatch countDownLatch, long timeoutMs,
113             String description) {
114         try {
115             if (!countDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
116                 throw new IllegalStateException(description + " timed out.");
117             }
118         } catch (InterruptedException e) {
119             Thread.currentThread().interrupt();
120             throw new IllegalStateException(description + " interrupted.", e);
121         }
122     }
123 
124     /**
125      * Calls {@link Slog#wtf} if a given lock is held.
126      */
127     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
128     public static void wtfIfLockHeld(String tag, Object lock) {
129         if (Thread.holdsLock(lock)) {
130             Slogf.wtf(tag, "Lock mustn't be held");
131         }
132     }
133 
134     /**
135      * Calls {@link Slog#wtf} if a given lock is not held.
136      */
137     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
138     public static void wtfIfLockNotHeld(String tag, Object lock) {
139         if (!Thread.holdsLock(lock)) {
140             Slogf.wtf(tag, "Lock must be held");
141         }
142     }
143 
144     private static class DirectExecutor implements Executor {
145 
146         @Override
147         public void execute(Runnable command) {
148             command.run();
149         }
150 
151         @Override
152         @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
153         public String toString() {
154             return "DIRECT_EXECUTOR";
155         }
156     }
157 }
158