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