[concurrency-interest] Unreported RuntimeException if Future.get() is never invoked

Tim Peierls tim at peierls.net
Thu Dec 21 12:00:52 EST 2006


It's a minor point, but I think it would be better to restore the interrupt
flag in the IE catch clause in your example, with

Thread.currentThread().interrupt();

Without this, if you run your FutureTask directly with ft.run(), an
interrupt of the current thread might be lost. I know that your intent is to
confine this FutureTask to the execute() call, but now you have an extra
constraint that maintainers of your code need to think about -- and what if
the ExecutorService implementation passed to the CancellableTask constructor
isn't as well-behaved as TPE? It's safer to abide by the interrupt handling
golden rule: propagate by throwing or restore the interrupt status.


Another way to handle unchecked exceptions and errors thrown by Runnables is
to wrap each submitted Runnable with a try-catch block. For example (off the
top of my head, uncompiled):

interface UncheckedHandler {
    caughtRuntimeException(Runnable r, RuntimeException e);
    caughtError(Runnable r, Error e);
}

class UncheckedHandlingTask implements Runnable {
    private final Runnable task;
    private final UncheckedHandler handler;

    public UncheckedHandlingTask(Runnable task, UncheckedHandler handler) {
        this.task = task;
        this.handler = handler;
    }

    public void run() {
        try {
            task.run ();
        } catch (RuntimeException e) {
            handler.caughtRuntimeException(task, e);
            throw e;
        } catch (Error e) {
            handler.caughtError(task, e);
            throw e;
        }
    }
}

In Java 6, you can override the newTaskFor method of TPE (from
AbstractExecutorService) to perform this wrapping automatically, which
effectively gives you the ability to bind unchecked exception and error
handling behavior to a TPE instance.

In Java 5, you can get the same effect by wrapping your ExecutorService in
an AbstractExecutorService that wraps submitted Runnables and delegates to
the TPE.

Still another approach is to override TPE's afterExecute method. This is
simpler than wrapping Runnables, but the approach in the preceding paragraph
can be applied to any ExecutorService, not just TPE.

--tim

On 12/21/06, Kevin Condon <conivek at gmail.com> wrote:
>
> A co-worker and I, JCiP in hand, discovered that the solution to this
> is to extend FutureTask overriding the done() hook to check/log the
> exception and then use exec.execute(futureTask) instead of
> submit(this).  To do this, change the execute() method:
>
>   public void execute() {
>     FutureTask<?> futureTask = new FutureTask<?>(this, null) {
>       protected void done() {
>         try {
>           get();
>         } catch (ExecutionException ex) {
>           ex.getCause().printStackTrace();  // logging or handling of your
> choice
>         } catch (CancellationException ex) {
>           // ignore unless you want to log task cancellation here
>         } catch (InterruptedException ex) {
>           // ignore; we're done, so get() won't block or be interrupted
>         }
>       }
>     };
>     future = futureTask;
>     exec.execute(futureTask);
>   }
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: /pipermail/attachments/20061221/3fb936eb/attachment.html 


More information about the Concurrency-interest mailing list