comments on Executor

Mark D. Anderson
Fri, 1 Feb 2002 15:13:03 -0800

A few comments on Executor based on my attempt to use dl's concurrent package in
a web application.
In the below I'm kind of intermixing the interfaces in the proposed java.util.concurrent
and in dl's  concurrent, but I believe they apply in either case.

1. task groups vs. executor groups.
It would be useful to have an object which groups callables (or futures or whatever) which
is different from the object which is executing the Executor strategy.
For example, in a web application I might have 30 tasks I can do in parallel to satisfy
a single web request. If that is the only request in the system, I might be ok with that.
But If I have multiple requests in the system, I want to constrain my total consumption.
If I create a new Executor for every request, I might for example constrain myself to
using 5 concurrent threads for those 30 tasks.
This approach is sub-optimal in light load, and will likely too aggressive for heavy load.
Maybe there is some clever way this can be done with concurrent, where I continue
to create a PooledExecutor per request, but I somehow get it to use a shared underlying
pool across all existing PooledExecutors.

Some of the operations on this callable group:
- I'd naturally like to be able to cancel all tasks in that callable group if I want to abandon
all processing of this particular request.
- I'd like to be able to configure it so that all tasks are cancelled if any of them fails (if I want that).
- I'd like timeout to be set at the group level.

2. More support for cancellation.
Right now, FutureResult has 3 possible states: 
   running = !isReady()
   finished_exception = (getException() != null)
   finished_ok = (getException() == null)
There is no easy way that I can see to determine that the future was killed.
(There is also no way to tell if the work on the result has started, by the way).
To tell if it was killed, I supposed i can check for:
  getException() != null && getException() instanceof InterruptedException
but that doesn't strike me as particularly clean.

Also, neither FutureResult nor Callable have a cancel() function that would be
used when the Executor shuts it down before it is ready.
If the result finishes on its own (even with its own exception other than InterruptedException),
then cleanup is its own duty. But if someone is going to shoot it in the head, it'd be
nice if they'd call the cancel() function first. This would be particularly helpful
for Callables that are wrappers to async interfaces.
Otherwise I would have to determine that they got killed, and find them, and clean
them up gracefully. Or trust a finalizer.

3. Accessors.
The Executor has no exposed way to iterate over its current Runnables.
The Future returned by Executor has no way to retrieve its underlying Callable.
These things might not bother me except for the issues in 1 and 2 -- to work around
them, i would need this. As it is, I end up building up parallel data structures duplicating
what must be on the inside of the implementation.