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