OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 package org.webrtc; | |
12 | |
13 import android.os.Handler; | |
14 import android.os.Looper; | |
15 import android.os.SystemClock; | |
16 import java.util.concurrent.Callable; | |
17 import java.util.concurrent.CountDownLatch; | |
18 import java.util.concurrent.TimeUnit; | |
19 | |
20 public class ThreadUtils { | |
21 /** | |
22 * Utility class to be used for checking that a method is called on the correc
t thread. | |
23 */ | |
24 public static class ThreadChecker { | |
25 private Thread thread = Thread.currentThread(); | |
26 | |
27 public void checkIsOnValidThread() { | |
28 if (thread == null) { | |
29 thread = Thread.currentThread(); | |
30 } | |
31 if (Thread.currentThread() != thread) { | |
32 throw new IllegalStateException("Wrong thread"); | |
33 } | |
34 } | |
35 | |
36 public void detachThread() { | |
37 thread = null; | |
38 } | |
39 } | |
40 | |
41 /** | |
42 * Throws exception if called from other than main thread. | |
43 */ | |
44 public static void checkIsOnMainThread() { | |
45 if (Thread.currentThread() != Looper.getMainLooper().getThread()) { | |
46 throw new IllegalStateException("Not on main thread!"); | |
47 } | |
48 } | |
49 | |
50 /** | |
51 * Utility interface to be used with executeUninterruptibly() to wait for bloc
king operations | |
52 * to complete without getting interrupted.. | |
53 */ | |
54 public interface BlockingOperation { void run() throws InterruptedException; } | |
55 | |
56 /** | |
57 * Utility method to make sure a blocking operation is executed to completion
without getting | |
58 * interrupted. This should be used in cases where the operation is waiting fo
r some critical | |
59 * work, e.g. cleanup, that must complete before returning. If the thread is i
nterrupted during | |
60 * the blocking operation, this function will re-run the operation until compl
etion, and only then | |
61 * re-interrupt the thread. | |
62 */ | |
63 public static void executeUninterruptibly(BlockingOperation operation) { | |
64 boolean wasInterrupted = false; | |
65 while (true) { | |
66 try { | |
67 operation.run(); | |
68 break; | |
69 } catch (InterruptedException e) { | |
70 // Someone is asking us to return early at our convenience. We can't can
cel this operation, | |
71 // but we should preserve the information and pass it along. | |
72 wasInterrupted = true; | |
73 } | |
74 } | |
75 // Pass interruption information along. | |
76 if (wasInterrupted) { | |
77 Thread.currentThread().interrupt(); | |
78 } | |
79 } | |
80 | |
81 public static boolean joinUninterruptibly(final Thread thread, long timeoutMs)
{ | |
82 final long startTimeMs = SystemClock.elapsedRealtime(); | |
83 long timeRemainingMs = timeoutMs; | |
84 boolean wasInterrupted = false; | |
85 while (timeRemainingMs > 0) { | |
86 try { | |
87 thread.join(timeRemainingMs); | |
88 break; | |
89 } catch (InterruptedException e) { | |
90 // Someone is asking us to return early at our convenience. We can't can
cel this operation, | |
91 // but we should preserve the information and pass it along. | |
92 wasInterrupted = true; | |
93 final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs; | |
94 timeRemainingMs = timeoutMs - elapsedTimeMs; | |
95 } | |
96 } | |
97 // Pass interruption information along. | |
98 if (wasInterrupted) { | |
99 Thread.currentThread().interrupt(); | |
100 } | |
101 return !thread.isAlive(); | |
102 } | |
103 | |
104 public static void joinUninterruptibly(final Thread thread) { | |
105 executeUninterruptibly(new BlockingOperation() { | |
106 @Override | |
107 public void run() throws InterruptedException { | |
108 thread.join(); | |
109 } | |
110 }); | |
111 } | |
112 | |
113 public static void awaitUninterruptibly(final CountDownLatch latch) { | |
114 executeUninterruptibly(new BlockingOperation() { | |
115 @Override | |
116 public void run() throws InterruptedException { | |
117 latch.await(); | |
118 } | |
119 }); | |
120 } | |
121 | |
122 public static boolean awaitUninterruptibly(CountDownLatch barrier, long timeou
tMs) { | |
123 final long startTimeMs = SystemClock.elapsedRealtime(); | |
124 long timeRemainingMs = timeoutMs; | |
125 boolean wasInterrupted = false; | |
126 boolean result = false; | |
127 do { | |
128 try { | |
129 result = barrier.await(timeRemainingMs, TimeUnit.MILLISECONDS); | |
130 break; | |
131 } catch (InterruptedException e) { | |
132 // Someone is asking us to return early at our convenience. We can't can
cel this operation, | |
133 // but we should preserve the information and pass it along. | |
134 wasInterrupted = true; | |
135 final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs; | |
136 timeRemainingMs = timeoutMs - elapsedTimeMs; | |
137 } | |
138 } while (timeRemainingMs > 0); | |
139 // Pass interruption information along. | |
140 if (wasInterrupted) { | |
141 Thread.currentThread().interrupt(); | |
142 } | |
143 return result; | |
144 } | |
145 | |
146 public static void waitUninterruptibly(final Object object) { | |
147 executeUninterruptibly(new BlockingOperation() { | |
148 @Override | |
149 public void run() throws InterruptedException { | |
150 object.wait(); | |
151 } | |
152 }); | |
153 } | |
154 | |
155 /** | |
156 * Post |callable| to |handler| and wait for the result. | |
157 */ | |
158 public static <V> V invokeAtFrontUninterruptibly( | |
159 final Handler handler, final Callable<V> callable) { | |
160 if (handler.getLooper().getThread() == Thread.currentThread()) { | |
161 try { | |
162 return callable.call(); | |
163 } catch (Exception e) { | |
164 throw new RuntimeException(e); | |
165 } | |
166 } | |
167 // Place-holder classes that are assignable inside nested class. | |
168 class CaughtException { | |
169 Exception e; | |
170 } | |
171 class Result { | |
172 public V value; | |
173 } | |
174 final Result result = new Result(); | |
175 final CaughtException caughtException = new CaughtException(); | |
176 final CountDownLatch barrier = new CountDownLatch(1); | |
177 handler.post(new Runnable() { | |
178 @Override | |
179 public void run() { | |
180 try { | |
181 result.value = callable.call(); | |
182 } catch (Exception e) { | |
183 caughtException.e = e; | |
184 } | |
185 barrier.countDown(); | |
186 } | |
187 }); | |
188 awaitUninterruptibly(barrier); | |
189 // Re-throw any runtime exception caught inside the other thread. Since this
is an invoke, add | |
190 // stack trace for the waiting thread as well. | |
191 if (caughtException.e != null) { | |
192 final RuntimeException runtimeException = new RuntimeException(caughtExcep
tion.e); | |
193 runtimeException.setStackTrace( | |
194 concatStackTraces(caughtException.e.getStackTrace(), runtimeException.
getStackTrace())); | |
195 throw runtimeException; | |
196 } | |
197 return result.value; | |
198 } | |
199 | |
200 /** | |
201 * Post |runner| to |handler|, at the front, and wait for completion. | |
202 */ | |
203 public static void invokeAtFrontUninterruptibly(final Handler handler, final R
unnable runner) { | |
204 invokeAtFrontUninterruptibly(handler, new Callable<Void>() { | |
205 @Override | |
206 public Void call() { | |
207 runner.run(); | |
208 return null; | |
209 } | |
210 }); | |
211 } | |
212 | |
213 static StackTraceElement[] concatStackTraces( | |
214 StackTraceElement[] inner, StackTraceElement[] outer) { | |
215 final StackTraceElement[] combined = new StackTraceElement[inner.length + ou
ter.length]; | |
216 System.arraycopy(inner, 0, combined, 0, inner.length); | |
217 System.arraycopy(outer, 0, combined, inner.length, outer.length); | |
218 return combined; | |
219 } | |
220 } | |
OLD | NEW |