comments on Executor

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


> Javadocs at:
>   http://gee.cs.oswego.edu/dl/concurrent/index.html
> Raw source at:
>   http://gee.cs.oswego.edu/dl/concurrent/java/util/concurrent/
> See class RunnableTask, as well as revised Executor and
> AbstractExecutor. (RunnableTask could stand having a better name.
> Suggestions welcome.) 

> Just this minimal support suffices in many cases -- if you want to
> track completion or be able to cancel execution before it starts, you
> would choose RunnableTask over Runnable.  But it cannot automatically
> deal with asynchronous interruption.  Cancellation status can only be
> checked automatically before execution, although programmers can also
> put in their own code inside their own run() methods to check
> isDone/isCancelled status.

I can't at the moment see where this interface is useful, since the interface has
no way to determine whether execution has started, and therefore it is
safe to cancel (according to your stated constraint that async interruption is
not supported).  So maybe it should then be called cancelIfNotStarted() or
cancelStart() or cancelRequest() instead of just cancel(). 

I feel a compulsion to ask for isStarted(), though I do recognize this only encourages
unsafe practices like:
  if (!r.isStarted()) r.cancel();

I've read some of your previous discussions on thread interruption, but  guess I'm
still missing something. An example of what I want to be able to do is something like this:

class CancellableRead extends Cancellable {
    java.net.Socket s_;
    java.io.InputStream is_;
    byte[] bytes_;

    CancellableRead(java.net.Socket s, byte[] bytes) {s_ = s; is_ = s_.getInputStream(); bytes_ = bytes;}
    public Object call(Object args) {read(); return null;}
    public void read() {is_.read(bytes);}
    public void cancelImpl() {s_.close();}
}

the Cancellable class is something like:

abstract class Cancellable {
   public abstract void cancelImpl();
   public abstract Object call(Object args);
   // for use by Executor or whoever is cancelling. will set cancel bit and call cancelImpl
   public void cancel();
   // for use either by subclass code or by external caller.
   public boolean isCancelled();
}

Now, even if you believe that socket closing is no more reliable than thread interruption,
this still makes for a lot of convenience.
Furthermore, if you look at what is coming up in jdk1.4 for java.nio.channels.Channel,
it appears that sun is promising async interruption of IO.
So when that is officially supported, I'd naturally like the new concurrent package to
interact with it. See:
http://java.sun.com/j2se/1.4/docs/api/java/nio/channels/InterruptibleChannel.html
It is unclear how this is supposed to work without fixing core JVM issues (it is certainly
more than just some new jsdk code right?). But it does seem like they are working on it.

To give another example, some servers offer a protocol-level cancel for async requests:

class CancellableAsync extends Cancellable {
    Object handle_;
    SomeRequest req_;

    CancellableAsync(SomeRequest req) {handle_ = null; req_ = req;}
    public Object call(Object args) {handle_ = req_.execute();}
    public void cancelImpl() {req_.cancel(handle_);}
}

Now it may very well be true that the software I'm calling downstream is lying when they
claim that they handle async cancellation.
But if they are not lying (or if they won't be lying in jdk1.4), then concurrent should
make it convenient to work with them.

btw, just to be clear, what I'd like is for the Executor or CompositeRunnable
to cancel the tasks, when I tell the container to do so.
In fact, I would think it would be easier to implement if the contract was that
if I've handed over a Runnable to an Executor, then I only cancel that Runnable
by calling an Executor method, not be messing with the Runnable directly.
I might mess directly with a FutureResult obtained from the Executor, but since
the Executor made it, it can hook what it is doing.

btw, whose responsibility would it be to protect against cancel() methods that
themselves block forever?

> I think you are looking for a CompositeRunnableTask (as in the GoF
> Composite pattern), where each of the elements can be run in parallel,
> but are cancellable as a unit. It would not be hard to build this
> on top of RunnableTask and a simple specialized Executor method. But
> it still faces the task-cancellation vs thread-interruption issues above.

.... and the fact that it still doesn't get me the critical code of how to share a pool
of threads among multiple composites.
I'd like to be able to have 4 composites of 5 tasks each to be all concurrent
if i have a shared pool of 20 threads (or 21 to include a house-keeping thread).
But if I have just 12 threads, i'd like that global limit to be enforced.
At this point, i don't care about the scheduling algorithm (give each of the 4 composites
3 threads, or run 2 composites to completion at a time).

> 
> > 3. Accessors.
> > The Executor has no exposed way to iterate over its current Runnables.
> 
> In PooledExecutor, the Runnables aren't necessarily all in one place
> at any given time.  Some Runnables are sitting in queues (see method
> drain()), while others are in the process of being dequeued and run.
> PooledExecutor doesn't itself have a way to iterate over them. It
> never needs to.  If applications need to track them, they have to
> record them in separately. Considering that different applications
> will want to specially track various Runnables for different
> application-specific reasons anyway, I think this is the right
> approach.

hmmm, well then maybe it'd help me if there were additional methods on Executor:
  executeResult(Future result);
  executeResults(Future[] results);
As it is, right now I have to maintain an array of both the original Callables,
and an array of the FutureResults.
If I won't be given a method where i can retrieve the underlying Callable from
a FutureResult (which would eliminate my first array), then let me construct
the FutureResult so i can subclass it with extra state, and provide it to the
Executor.

-mda