From 7e4b604cda3af5147b391e78abf0639f7094b0d9 Mon Sep 17 00:00:00 2001 From: magjed Date: Thu, 8 Sep 2016 05:06:31 -0700 Subject: [PATCH] Android ThreadUtils: Propagate exceptions in invoke functions Right now, RuntimeExceptions thrown in an invoke function will kill the handler thread. This CL propagates the exceptions to the calling thread instead, to give the caller a change to handle them. BUG=webrtc:6327 Review-Url: https://codereview.webrtc.org/2316223002 Cr-Commit-Position: refs/heads/master@{#14132} --- .../base/java/src/org/webrtc/ThreadUtils.java | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/webrtc/base/java/src/org/webrtc/ThreadUtils.java b/webrtc/base/java/src/org/webrtc/ThreadUtils.java index cf814b0f53..b7ec8fb120 100644 --- a/webrtc/base/java/src/org/webrtc/ThreadUtils.java +++ b/webrtc/base/java/src/org/webrtc/ThreadUtils.java @@ -151,56 +151,63 @@ public class ThreadUtils { public static V invokeAtFrontUninterruptibly( final Handler handler, final Callable callable) { if (handler.getLooper().getThread() == Thread.currentThread()) { - V value; try { - value = callable.call(); + return callable.call(); } catch (Exception e) { - final RuntimeException runtimeException = - new RuntimeException("Callable threw exception: " + e); - runtimeException.setStackTrace(e.getStackTrace()); - throw runtimeException; + throw new RuntimeException(e); } - return value; + } + // Place-holder classes that are assignable inside nested class. + class CaughtException { + Exception e; } class Result { public V value; } final Result result = new Result(); + final CaughtException caughtException = new CaughtException(); final CountDownLatch barrier = new CountDownLatch(1); handler.post(new Runnable() { @Override public void run() { try { result.value = callable.call(); } catch (Exception e) { - final RuntimeException runtimeException = - new RuntimeException("Callable threw exception: " + e); - runtimeException.setStackTrace(e.getStackTrace()); - throw runtimeException; + caughtException.e = e; } barrier.countDown(); } }); awaitUninterruptibly(barrier); + // Re-throw any runtime exception caught inside the other thread. Since this is an invoke, add + // stack trace for the waiting thread as well. + if (caughtException.e != null) { + final RuntimeException runtimeException = new RuntimeException(caughtException.e); + runtimeException.setStackTrace(concatStackTraces( + caughtException.e.getStackTrace(), + runtimeException.getStackTrace())); + throw runtimeException; + } return result.value; } /** - * Post |runner| to |handler|, at the front, and wait for - * completion. + * Post |runner| to |handler|, at the front, and wait for completion. */ public static void invokeAtFrontUninterruptibly(final Handler handler, final Runnable runner) { - if (handler.getLooper().getThread() == Thread.currentThread()) { - runner.run(); - return; - } - final CountDownLatch barrier = new CountDownLatch(1); - handler.postAtFrontOfQueue(new Runnable() { + invokeAtFrontUninterruptibly(handler, new Callable() { @Override - public void run() { + public Void call() { runner.run(); - barrier.countDown(); + return null; } }); - awaitUninterruptibly(barrier); + } + + private static StackTraceElement[] concatStackTraces( + StackTraceElement[] inner, StackTraceElement[] outer) { + final StackTraceElement[] combined = new StackTraceElement[inner.length + outer.length]; + System.arraycopy(inner, 0, combined, 0, inner.length); + System.arraycopy(outer, 0, combined, inner.length, outer.length); + return combined; } }