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