Home | History | Annotate | Download | only in base
      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