Executors, Futures, and Cancelation

M

mievlecl

Hello. I am working with the java.util.concurrent API introduced in
Java 1.5. Specifically, I will talk about Executors, ExecutorServices
and Futures.

So far, I have gotten a lot of use out of the API, and used it on a
number of projects. However, I am consistently running into a problem
for which I have not found a good solution.

Callables submitted to an ExecutorService return a reference to a
Future object. This future object gives you some modest ways to
interact with the task: a method to cancel the task (cancel), methods
to query the status of the task (isDone/isCancelled), and methods to
get the result of the task's processing (get).

The problem is as follows:

Say you wish to cancel some long-running task that you know is queued
for execution. Let's say you do this with, say:

future.cancel(true); // true allows the executor to use an interrupt
to help cancel the task

The trouble here, is, now that we have cancelled the task, we have no
way of determining if the task was cancelled before running, or if it
was cancelled while running.

Let us say that there's something we always need to do after this
task. Let's call "something" a method: "myCleanup()".

By "after this task," I mean we always need to this "myCleanup()"
whether the task ran to completion or was cancelled. So, there should
be a one-to-one relationships between enqueues of the task, and calls
to "myCleanup()".

For the sake of serial execution, we put the call to "myCleanup()" in
the task code itself. So the core of the task code looks something
like:

performWhateverThisTaskDoes();
cleanup();

However, it is possible that the call to --

future.cancel(true);

-- will prevent this task from ever being executed. This is
especially true if, for example, our executor has a sizable task
backlog in its work queue. That's OK. We asked for the task to be
cancelled, and we got it.

The trouble comes when you realize (or already know) that --

future.cancel(true);

-- does not always stop the task from executing. If the task is "in
the process of executing," cancelling it will actually normally do
nothing -- the task will happily run to completion. This is OK and I
have come to terms with this behavior.

The crux of the problem is there's no way to figure out what happened
-- what state the task as in, when it was cancelled. I have no idea
if I should expect that the task is "in progress" and will eventually
call myCleanup() for me, or if it is still "in queue," and will never
execute, because it's been cancelled before any executor even set eyes
on it.

Assume, for example purposes, that because of the nature of the
myCleanup() method, it is undesirable or incorrect to call it twice
per task enqueued.

This leaves me in a tricky spot: I've cancelled the task, but I have
no way of knowing if the task was cancelled "pre-execution" or "mid-
execution." It's important for me to know, so that I can know whether
"myCleanup()" needs to be called manually, or if it will be handled by
the task.

To me, it seems a fairly natural thing to be able to cancel tasks. It
also seems like a fairly useful piece of information to know if that
task was cancelled before beginning, or was cancelled while running --
since the two scenarios can leave the software in very different
states. Yet, as far as I can see, there is no way to determine this
information.

You may suggest to me the isDone/isCancelled Future API methods.
Please note that there is actually no way to determine whether a task
actually ran or was cancelled before running by using the information
retrieved from these methods. Nor is the return value from
Future.cancel() informative in these situations.

I have also tried extending FutureTask and overriding #done(). This
method is not called if your task is cancelled before execution. As
is it is not possible to efficiently respond to the "absence" of a
method call, this is not a very good way to determine if a task will
or will not run.

Any ideas?
 
L

Lew

Hello. I am working with the java.util.concurrent API introduced in
Java 1.5. Specifically, I will talk about Executors, ExecutorServices
and Futures.

This is a bit complex of a topic, but it's thoroughly handled in the book
/Java Concurrency in Practice/, by Brian Goetz et al. Buy it, study it.
 
M

mievlecl

This is a bit complex of a topic, but it's thoroughly handled in the book
/Java Concurrency in Practice/, by Brian Goetz et al. Buy it, study it.

Thanks for the reply. I actually own this book, and have been telling
my co-workers about it almost daily. It's also sitting open on my
desk right now. To be honest, I thought I had gleaned most that I
could be from it; however, I may well be missing something. A re-read
wouldn't hurt me, so I think I will. But I am still interested in
hearing others' first-hand experience with such issues.

thanks,

Mike
 
D

Daniel Pitts

Thanks for the reply. I actually own this book, and have been telling
my co-workers about it almost daily. It's also sitting open on my
desk right now. To be honest, I thought I had gleaned most that I
could be from it; however, I may well be missing something. A re-read
wouldn't hurt me, so I think I will. But I am still interested in
hearing others' first-hand experience with such issues.

thanks,

Mike
Its kind of funny you should bring this up now, a coworker of mine is in
the same situation.

My suggestion to him was that he might be able to "not care" if the task
had started yet. It's mostly an IO bound loop that would be interrupted,
and we wouldn't care after that point.

If you're process dictates that you know what has started, etc... I
would suggest passing to your Callable objects something that they can
manipulate to give feedback on their state (started, not started,
successfully canceled, etc...). This isn't a trivial task, but it isn't
insurmountable either. You'll have to make sure you do proper analysis
on the concurrent code that access this object.
Also note that Future.cancel will return a boolean value whether it
succeeds in canceling or not.
<http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html#cancel(boolean)>

Hopefully, this helps you.
 
M

Mark Space

Daniel said:
Also note that Future.cancel will return a boolean value whether it
succeeds in canceling or not.

I've never used the Future object, but I notice the API says that get()
and get( timeout, timeunit) return a CanceledException if the task has
been canceled. The latter version of get could be used with a short
timeout to test is a Future object belongs to a task that got canceled.
 
M

Mark Space

Mark said:
I've never used the Future object, but I notice the API says that get()
and get( timeout, timeunit) return a CanceledException if the task has
been canceled. The latter version of get could be used with a short
timeout to test is a Future object belongs to a task that got canceled.

Hmm, not to mention the presence of an isCanceled() method....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top