summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
blob: 00625800c91ea6ac87638be5075f8b3266eecc45 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.util;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import org.mozilla.gecko.annotation.RobocopTarget;

public final class ThreadUtils {
  private static final String LOGTAG = "ThreadUtils";

  /**
   * Controls the action taken when a method like {@link
   * ThreadUtils#assertOnUiThread(AssertBehavior)} detects a problem.
   */
  public enum AssertBehavior {
    NONE,
    THROW,
  }

  private static final Thread sUiThread = Looper.getMainLooper().getThread();
  private static final Handler sUiHandler = new Handler(Looper.getMainLooper());

  // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra
  // function call of the getter was harming performance. (Bug 897123))
  // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise
  // this out at compile time.
  public static Handler sGeckoHandler;
  public static volatile Thread sGeckoThread;

  public static Thread getUiThread() {
    return sUiThread;
  }

  public static Handler getUiHandler() {
    return sUiHandler;
  }

  /**
   * Runs the provided runnable on the UI thread. If this method is called on the UI thread the
   * runnable will be executed synchronously.
   *
   * @param runnable the runnable to be executed.
   */
  public static void runOnUiThread(final Runnable runnable) {
    // We're on the UI thread already, let's just run this
    if (isOnUiThread()) {
      runnable.run();
      return;
    }

    postToUiThread(runnable);
  }

  public static void postToUiThread(final Runnable runnable) {
    sUiHandler.post(runnable);
  }

  public static void postToUiThreadDelayed(final Runnable runnable, final long delayMillis) {
    sUiHandler.postDelayed(runnable, delayMillis);
  }

  public static void removeUiThreadCallbacks(final Runnable runnable) {
    sUiHandler.removeCallbacks(runnable);
  }

  public static Handler getBackgroundHandler() {
    return GeckoBackgroundThread.getHandler();
  }

  public static void postToBackgroundThread(final Runnable runnable) {
    GeckoBackgroundThread.post(runnable);
  }

  public static void assertOnUiThread(final AssertBehavior assertBehavior) {
    assertOnThread(getUiThread(), assertBehavior);
  }

  public static void assertOnUiThread() {
    assertOnThread(getUiThread(), AssertBehavior.THROW);
  }

  @RobocopTarget
  public static void assertOnGeckoThread() {
    assertOnThread(sGeckoThread, AssertBehavior.THROW);
  }

  public static void assertOnThread(final Thread expectedThread, final AssertBehavior behavior) {
    assertOnThreadComparison(expectedThread, behavior, true);
  }

  private static void assertOnThreadComparison(
      final Thread expectedThread, final AssertBehavior behavior, final boolean expected) {
    final Thread currentThread = Thread.currentThread();
    final long currentThreadId = currentThread.getId();
    final long expectedThreadId = expectedThread.getId();

    if ((currentThreadId == expectedThreadId) == expected) {
      return;
    }

    final String message;
    if (expected) {
      message =
          "Expected thread "
              + expectedThreadId
              + " (\""
              + expectedThread.getName()
              + "\"), but running on thread "
              + currentThreadId
              + " (\""
              + currentThread.getName()
              + "\")";
    } else {
      message =
          "Expected anything but "
              + expectedThreadId
              + " (\""
              + expectedThread.getName()
              + "\"), but running there.";
    }

    final IllegalThreadStateException e = new IllegalThreadStateException(message);

    switch (behavior) {
      case THROW:
        throw e;
      default:
        Log.e(LOGTAG, "Method called on wrong thread!", e);
    }
  }

  public static boolean isOnUiThread() {
    return isOnThread(getUiThread());
  }

  @RobocopTarget
  public static boolean isOnThread(final Thread thread) {
    return (Thread.currentThread().getId() == thread.getId());
  }
}