comments on Executor

Mark D. Anderson mda@discerning.com
Sat, 2 Feb 2002 14:09:55 -0800


> If you do want something heavier than a simple cancelled bit,
> you can actually interrupt the thread inside cancel. As in:
> ...

i think we need to clarify purposes, perhaps by changing the function names:
1. a cancel() which is a method somehow connected to an Executor which cancels
a started task. This means it should:
- do the appropriate things in the Executor data structures
- call any hook methods in the particular runnable/future
- carry out whatever the Executor's thread interruption policy is (interrupt, kill, etc.)
2. like (1), except for a task that has no yet been started. Its behavior is similar to (1).
3. a cancel() which is the hook method of a particular Callable/Future subclass,
which implements how it'd like to cancel an operation that has been started.

An implementation of cancel() that just sets a bit doesn't accomplish any of the above.

>   public synchronized void cancel() {
>     if (!isDone()) return;
>     super.cancel();
>     if (runner != null)
>       runner.interrupt();
>   }
> }

to amplify, i don't see how the above could work with an arbitrary Executor when it
has no idea, when it is calling a Callable.cancel(), whether it does something as innocuous as
setting a bit, or as severe as interrupting.
unless, that is, runner.interrupt() is somehow connected to the Executor instance,
so it can tell after calling cancel() that it shouldn't do an interrupt as well.
Or maybe cancel() could return a boolean indicating that it has done the interrupt,
or a boolean indicating that the caller may do an interrupt.
Or maybe RunnableTask could have an isInterrupted() bit. Or something like that.

> As mentioned before, you need to be sure that it is OK to actually
> interrupt the thread, no matter what context the Runnable finds itself
> being run in.

In my mind, having a cancel() hook makes thread interruption more viable.
The Executor can do something like this:
- call cancel()
- check if the future is done now.
it might be, because the cancel might have
interrupted or otherwise caused the run() to throw an exception.
- if the future is still not done, and the executor configuration says so (or cancel() return value,
or !task.isInterrupted()), then interrupt it.

Thus there is sort of an escalation, where cancel() can be used for a graceful termination,
and if that doesn't work, the more severe steps can be taken.

BTW, is there a reason that RunnableTask has an isDone(), while Future has a isAvailable()?

> I was on the nio JSR expert group. Yes, IO on nio channels really is
> interruptible. If you discover otherwise, submit a bug report!

I haven't touched the jdk1.4 yet, sorry.

since you are certainly quite familiar with both the nio and concurrent projects :),
how would you for example perform concurrent reads from 5 channels
that implement both ReadableByteChannel and InterruptibleChannel,
using an Executor?

might as well get started on that wonderful documentation now :).

> I'm not sure what you mean here. Suppose you have something like:
> 
> class CompositeTask {
>   void runAll(Executor exec) {
>     for each task 
>       exec.execute(task)
>   }
>   void cancelAll() {
>     for each task 
>       task.cancel();
>   }
> }
> 
> So you can use any number of CompositeTasks with the same executor,
> and cancel each composite independently. Is there something else you
> need?

Well, for example, I need a way to wait up to msecs on just the tasks in the CompositeTask.
The methods shutdownAfterProcessingCurrentlyQueuedTasks() and
awaitTerminationAfterShutdown(msecs) apply to everything in the Executor.

I'm also not clear (as per top of this message) whether task.cancel() is seen by the Executor
or not.

-mda