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