12

I want to wait two tasks to finish then return the result of them but sometimes I get this error. Why? Where did CancellationException come from?

public class ShouldVoteTask extends AbstractWorkerTask<Void, Void, Boolean> {
    private final int placeID; 
    private final int userID;

    public ShouldVoteTask(final int placeID, final int userID) {
        this.placeID = placeID;
        this.userID = userID;
    }

    @Override
    protected Boolean doInBackground(final Void... params) {
        try {
            // Prepare callables.
            final IsMaxRatingCallable call1 = new IsMaxRatingCallable(placeID);
            final DidVoteCallable call2 = new DidVoteCallable(placeID, userID);         
            final List<Callable<Boolean>> callables = new ArrayList<Callable<Boolean>>();           
            callables.add(call1);
            callables.add(call2);

            // Execute them.
            final ExecutorService service = Executors.newFixedThreadPool(2);            
            final List<Future<Boolean>> futures = service.invokeAll(callables, 5, TimeUnit.SECONDS);

            // Check the result.
            boolean result = true;
            for(final Future<Boolean> future : futures) {
                if(future.get()) {
                    result = false;
                }
            }

            return result;
        } catch (final InterruptedException e) {
            e.printStackTrace();
        } catch (final ExecutionException e) {
            e.printStackTrace();
        }
        return false;
    }
}

private class IsMaxRatingCallable implements Callable<Boolean> {
    private final int placeID;

    public IsMaxRatingCallable(final int placeID) {
        this.placeID = placeID;
    }

    @Override
    public Boolean call() throws Exception {
        return Places.isMaxRating(placeID);         
    }
}

private class DidVoteCallable implements Callable<Boolean> {
    private final int placeID;
    private final int userID;

    public DidVoteCallable(final int placeID, final int userID) {
        this.placeID = placeID;
        this.userID = userID;
    }

    @Override
    public Boolean call() throws Exception {
        return Votes.didVote(placeID, userID);          
    }
}

Error

E/AndroidRuntime(19014): FATAL EXCEPTION: AsyncTask #1
E/AndroidRuntime(19014): java.lang.RuntimeException: An error occured while executing doInBackground()
E/AndroidRuntime(19014):    at android.os.AsyncTask$3.done(AsyncTask.java:200)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
E/AndroidRuntime(19014):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
E/AndroidRuntime(19014):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
E/AndroidRuntime(19014):    at java.lang.Thread.run(Thread.java:1027)
E/AndroidRuntime(19014): Caused by: java.util.concurrent.CancellationException
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
E/AndroidRuntime(19014):    at com.vfa.android.planet.task.ShouldVoteTask.doInBackground(ShouldVoteTask.java:43)
E/AndroidRuntime(19014):    at com.vfa.android.planet.task.ShouldVoteTask.doInBackground(ShouldVoteTask.java:1)
E/AndroidRuntime(19014):    at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/AndroidRuntime(19014):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
E/AndroidRuntime(19014):    ... 4 more
emeraldhieu
  • 9,380
  • 19
  • 81
  • 139

1 Answers1

27

You ask your executor service to execute your callables with a timeout of 5 seconds. According to the javadoc:

tasks that have not completed [by the end of the timeout] are cancelled

My guess is that future.get() throws a CancellationException because the timeout has been reached and the executor calls future.cancel().

You could either:

  • increase the timeout
  • catch an InterruptedException in your callable to handle the cancellation gracefully
assylias
  • 321,522
  • 82
  • 660
  • 783
  • But `CancellationException` does not extend `InterruptedException` right? – Luís Soares Dec 04 '17 at 20:40
  • BTW, I'm treating `InterruptedException` around `invokeAll` and still get that `CancellationException` – Luís Soares Dec 04 '17 at 20:42
  • @LuísSoares No it doesn't. – assylias Dec 04 '17 at 21:05
  • @assylas: so when you recommend to "catch an `InterruptedException`" you refer around each `get` and not around `invokeAll` right? – Luís Soares Dec 04 '17 at 21:51
  • 1
    @LuísSoares No I meant within the call method: if `future.cancel()` is called, an interruption is sent to the executing thread - in your callable, you can check the thread interrupted flag to see if it has been interrupted. In the example given in the question, it's unclear if it's possible to do that or not. – assylias Dec 04 '17 at 22:02