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