1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import android.os.Handler; 8 import android.os.Looper; 9 import android.os.Process; 10 11 import org.chromium.base.annotations.CalledByNative; 12 13 import java.util.concurrent.Callable; 14 import java.util.concurrent.ExecutionException; 15 import java.util.concurrent.FutureTask; 16 17 /** 18 * Helper methods to deal with threading related tasks. 19 */ 20 public class ThreadUtils { 21 22 private static final Object sLock = new Object(); 23 24 private static boolean sWillOverride = false; 25 26 private static Handler sUiThreadHandler = null; 27 28 public static void setWillOverrideUiThread() { 29 synchronized (sLock) { 30 sWillOverride = true; 31 } 32 } 33 34 @VisibleForTesting 35 public static void setUiThread(Looper looper) { 36 synchronized (sLock) { 37 if (looper == null) { 38 // Used to reset the looper after tests. 39 sUiThreadHandler = null; 40 return; 41 } 42 if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { 43 throw new RuntimeException("UI thread looper is already set to " 44 + sUiThreadHandler.getLooper() + " (Main thread looper is " 45 + Looper.getMainLooper() + "), cannot set to new looper " + looper); 46 } else { 47 sUiThreadHandler = new Handler(looper); 48 } 49 } 50 } 51 52 private static Handler getUiThreadHandler() { 53 synchronized (sLock) { 54 if (sUiThreadHandler == null) { 55 if (sWillOverride) { 56 throw new RuntimeException("Did not yet override the UI thread"); 57 } 58 sUiThreadHandler = new Handler(Looper.getMainLooper()); 59 } 60 return sUiThreadHandler; 61 } 62 } 63 64 /** 65 * Run the supplied Runnable on the main thread. The method will block until the Runnable 66 * completes. 67 * 68 * @param r The Runnable to run. 69 */ 70 public static void runOnUiThreadBlocking(final Runnable r) { 71 if (runningOnUiThread()) { 72 r.run(); 73 } else { 74 FutureTask<Void> task = new FutureTask<Void>(r, null); 75 postOnUiThread(task); 76 try { 77 task.get(); 78 } catch (Exception e) { 79 throw new RuntimeException("Exception occured while waiting for runnable", e); 80 } 81 } 82 } 83 84 /** 85 * Run the supplied Callable on the main thread, wrapping any exceptions in a RuntimeException. 86 * The method will block until the Callable completes. 87 * 88 * @param c The Callable to run 89 * @return The result of the callable 90 */ 91 @VisibleForTesting 92 public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) { 93 try { 94 return runOnUiThreadBlocking(c); 95 } catch (ExecutionException e) { 96 throw new RuntimeException("Error occured waiting for callable", e); 97 } 98 } 99 100 /** 101 * Run the supplied Callable on the main thread, The method will block until the Callable 102 * completes. 103 * 104 * @param c The Callable to run 105 * @return The result of the callable 106 * @throws ExecutionException c's exception 107 */ 108 public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException { 109 FutureTask<T> task = new FutureTask<T>(c); 110 runOnUiThread(task); 111 try { 112 return task.get(); 113 } catch (InterruptedException e) { 114 throw new RuntimeException("Interrupted waiting for callable", e); 115 } 116 } 117 118 /** 119 * Run the supplied FutureTask on the main thread. The method will block only if the current 120 * thread is the main thread. 121 * 122 * @param task The FutureTask to run 123 * @return The queried task (to aid inline construction) 124 */ 125 public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) { 126 if (runningOnUiThread()) { 127 task.run(); 128 } else { 129 postOnUiThread(task); 130 } 131 return task; 132 } 133 134 /** 135 * Run the supplied Callable on the main thread. The method will block only if the current 136 * thread is the main thread. 137 * 138 * @param c The Callable to run 139 * @return A FutureTask wrapping the callable to retrieve results 140 */ 141 public static <T> FutureTask<T> runOnUiThread(Callable<T> c) { 142 return runOnUiThread(new FutureTask<T>(c)); 143 } 144 145 /** 146 * Run the supplied Runnable on the main thread. The method will block only if the current 147 * thread is the main thread. 148 * 149 * @param r The Runnable to run 150 */ 151 public static void runOnUiThread(Runnable r) { 152 if (runningOnUiThread()) { 153 r.run(); 154 } else { 155 getUiThreadHandler().post(r); 156 } 157 } 158 159 /** 160 * Post the supplied FutureTask to run on the main thread. The method will not block, even if 161 * called on the UI thread. 162 * 163 * @param task The FutureTask to run 164 * @return The queried task (to aid inline construction) 165 */ 166 public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) { 167 getUiThreadHandler().post(task); 168 return task; 169 } 170 171 /** 172 * Post the supplied Runnable to run on the main thread. The method will not block, even if 173 * called on the UI thread. 174 * 175 * @param task The Runnable to run 176 */ 177 public static void postOnUiThread(Runnable task) { 178 getUiThreadHandler().post(task); 179 } 180 181 /** 182 * Post the supplied Runnable to run on the main thread after the given amount of time. The 183 * method will not block, even if called on the UI thread. 184 * 185 * @param task The Runnable to run 186 * @param delayMillis The delay in milliseconds until the Runnable will be run 187 */ 188 @VisibleForTesting 189 public static void postOnUiThreadDelayed(Runnable task, long delayMillis) { 190 getUiThreadHandler().postDelayed(task, delayMillis); 191 } 192 193 /** 194 * Asserts that the current thread is running on the main thread. 195 */ 196 public static void assertOnUiThread() { 197 if (BuildConfig.DCHECK_IS_ON && !runningOnUiThread()) { 198 throw new IllegalStateException("Must be called on the Ui thread."); 199 } 200 } 201 202 /** 203 * @return true iff the current thread is the main (UI) thread. 204 */ 205 public static boolean runningOnUiThread() { 206 return getUiThreadHandler().getLooper() == Looper.myLooper(); 207 } 208 209 public static Looper getUiThreadLooper() { 210 return getUiThreadHandler().getLooper(); 211 } 212 213 /** 214 * Set thread priority to audio. 215 */ 216 @CalledByNative 217 public static void setThreadPriorityAudio(int tid) { 218 Process.setThreadPriority(tid, Process.THREAD_PRIORITY_AUDIO); 219 } 220 221 /** 222 * Checks whether Thread priority is THREAD_PRIORITY_AUDIO or not. 223 * @param tid Thread id. 224 * @return true for THREAD_PRIORITY_AUDIO and false otherwise. 225 */ 226 @CalledByNative 227 private static boolean isThreadPriorityAudio(int tid) { 228 return Process.getThreadPriority(tid) == Process.THREAD_PRIORITY_AUDIO; 229 } 230 } 231