diff --git a/bolts-tasks/src/main/java/bolts/ExecutorException.java b/bolts-tasks/src/main/java/bolts/ExecutorException.java new file mode 100644 index 0000000..cc9486d --- /dev/null +++ b/bolts-tasks/src/main/java/bolts/ExecutorException.java @@ -0,0 +1,11 @@ +package bolts; + +/** + * This is a wrapper class for emphasizing that task failed due to bad Executor, rather than the continuation block it self. + */ +public class ExecutorException extends RuntimeException { + + public ExecutorException(Exception e) { + super("An exception was thrown by a Executor", e); + } +} diff --git a/bolts-tasks/src/main/java/bolts/Task.java b/bolts-tasks/src/main/java/bolts/Task.java index aa72632..4bcaa95 100644 --- a/bolts-tasks/src/main/java/bolts/Task.java +++ b/bolts-tasks/src/main/java/bolts/Task.java @@ -294,23 +294,28 @@ public static Task call(final Callable callable, Exe public static Task call(final Callable callable, Executor executor, final CancellationToken ct) { final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); - executor.execute(new Runnable() { - @Override - public void run() { - if (ct != null && ct.isCancellationRequested()) { - tcs.setCancelled(); - return; - } + try { + executor.execute(new Runnable() { + @Override + public void run() { + if (ct != null && ct.isCancellationRequested()) { + tcs.setCancelled(); + return; + } - try { - tcs.setResult(callable.call()); - } catch (CancellationException e) { - tcs.setCancelled(); - } catch (Exception e) { - tcs.setError(e); + try { + tcs.setResult(callable.call()); + } catch (CancellationException e) { + tcs.setCancelled(); + } catch (Exception e) { + tcs.setError(e); + } } - } - }); + }); + } catch (Exception e) { + tcs.setError(new ExecutorException(e)); + } + return tcs.getTask(); } @@ -800,24 +805,28 @@ private static void completeImmediately( final bolts.TaskCompletionSource tcs, final Continuation continuation, final Task task, Executor executor, final CancellationToken ct) { - executor.execute(new Runnable() { - @Override - public void run() { - if (ct != null && ct.isCancellationRequested()) { - tcs.setCancelled(); - return; - } + try { + executor.execute(new Runnable() { + @Override + public void run() { + if (ct != null && ct.isCancellationRequested()) { + tcs.setCancelled(); + return; + } - try { - TContinuationResult result = continuation.then(task); - tcs.setResult(result); - } catch (CancellationException e) { - tcs.setCancelled(); - } catch (Exception e) { - tcs.setError(e); + try { + TContinuationResult result = continuation.then(task); + tcs.setResult(result); + } catch (CancellationException e) { + tcs.setCancelled(); + } catch (Exception e) { + tcs.setError(e); + } } - } - }); + }); + } catch (Exception e) { + tcs.setError(new ExecutorException(e)); + } } /** @@ -841,45 +850,49 @@ private static void completeAfterTask( final Continuation> continuation, final Task task, final Executor executor, final CancellationToken ct) { - executor.execute(new Runnable() { - @Override - public void run() { - if (ct != null && ct.isCancellationRequested()) { - tcs.setCancelled(); - return; - } + try { + executor.execute(new Runnable() { + @Override + public void run() { + if (ct != null && ct.isCancellationRequested()) { + tcs.setCancelled(); + return; + } - try { - Task result = continuation.then(task); - if (result == null) { - tcs.setResult(null); - } else { - result.continueWith(new Continuation() { - @Override - public Void then(Task task) { - if (ct != null && ct.isCancellationRequested()) { - tcs.setCancelled(); + try { + Task result = continuation.then(task); + if (result == null) { + tcs.setResult(null); + } else { + result.continueWith(new Continuation() { + @Override + public Void then(Task task) { + if (ct != null && ct.isCancellationRequested()) { + tcs.setCancelled(); + return null; + } + + if (task.isCancelled()) { + tcs.setCancelled(); + } else if (task.isFaulted()) { + tcs.setError(task.getError()); + } else { + tcs.setResult(task.getResult()); + } return null; } - - if (task.isCancelled()) { - tcs.setCancelled(); - } else if (task.isFaulted()) { - tcs.setError(task.getError()); - } else { - tcs.setResult(task.getResult()); - } - return null; - } - }); + }); + } + } catch (CancellationException e) { + tcs.setCancelled(); + } catch (Exception e) { + tcs.setError(e); } - } catch (CancellationException e) { - tcs.setCancelled(); - } catch (Exception e) { - tcs.setError(e); } - } - }); + }); + } catch (Exception e) { + tcs.setError(new ExecutorException(e)); + } } private void runContinuations() { diff --git a/bolts-tasks/src/test/java/bolts/TaskTest.java b/bolts-tasks/src/test/java/bolts/TaskTest.java index 910a867..9532b99 100644 --- a/bolts-tasks/src/test/java/bolts/TaskTest.java +++ b/bolts-tasks/src/test/java/bolts/TaskTest.java @@ -19,6 +19,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.Executors; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -1026,6 +1027,91 @@ public Void then(Task task) throws Exception { }); } + @Test + public void testCallWithBadExecutor() { + final RuntimeException exception = new RuntimeException("BAD EXECUTORS"); + + Task.call(new Callable() { + public Integer call() throws Exception { + return 1; + } + }, new Executor() { + @Override + public void execute(Runnable command) { + throw exception; + } + }).continueWith(new Continuation() { + @Override + public Object then(Task task) throws Exception { + assertTrue(task.isFaulted()); + assertTrue(task.getError() instanceof ExecutorException); + assertEquals(exception, task.getError().getCause()); + + return null; + } + }); + } + + @Test + public void testContinueWithBadExecutor() { + final RuntimeException exception = new RuntimeException("BAD EXECUTORS"); + + Task.call(new Callable() { + public Integer call() throws Exception { + return 1; + } + }).continueWith(new Continuation() { + @Override + public Integer then(Task task) throws Exception { + return task.getResult(); + } + }, new Executor() { + @Override + public void execute(Runnable command) { + throw exception; + } + }).continueWith(new Continuation() { + @Override + public Object then(Task task) throws Exception { + assertTrue(task.isFaulted()); + assertTrue(task.getError() instanceof ExecutorException); + assertEquals(exception, task.getError().getCause()); + + return null; + } + }); + } + + @Test + public void testContinueWithTaskAndBadExecutor() { + final RuntimeException exception = new RuntimeException("BAD EXECUTORS"); + + Task.call(new Callable() { + public Integer call() throws Exception { + return 1; + } + }).continueWithTask(new Continuation>() { + @Override + public Task then(Task task) throws Exception { + return task; + } + }, new Executor() { + @Override + public void execute(Runnable command) { + throw exception; + } + }).continueWith(new Continuation() { + @Override + public Object then(Task task) throws Exception { + assertTrue(task.isFaulted()); + assertTrue(task.getError() instanceof ExecutorException); + assertEquals(exception, task.getError().getCause()); + + return null; + } + }); + } + //region TaskCompletionSource @Test