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