logo       

Re: JCiP Memoizer: msg#00034

java.jsr.166-concurrency

Subject: Re: JCiP Memoizer

Hi Tim,

You don't need the interrupted() check in the Callable adapter. If the thread
was interrupted during call() then if call() "aborted" it should have thrown
InterruptedException. The fact that call() didn't throw indicates that call()
completed successfully and the result is available.

What we'd really like is to specialise FutureTask.run so that
InterruptedException is treated as a cancel request. But there is no way to do
that without writing your own Future implementation because any attempt to
"wrap" super.run() is too late to stop the IE from being treated as a general
exception. Hmmm - with the fixes in the Java 6 version (I think they got in!)
that ensures sync.innerRun calls ft.setException then it might be possible to
do this:

ft = new FutureTask(eval) {
protected void setException(Throwable t) {
if (t instanceof InterruptedException)
cancel(false);
else
super.setException(t);
}
};

Still not "pretty" though.

The complexity has arisen in this example because of the attempt to treat
interruption as a transient error rather than a permanent one, combined with
the desire to show handling cancellation even though it isn't actually possible.

Cheers,
David




> Tim Peierls <tim@xxxxxxxxxxx> wrote:
>
> One of the things that makes the Memoizer example so interesting (and
> suitable for inclusion in chapter 5 of JCiP) is that it is a rare
> example of
> FutureTask used outside of an Executor.
>
> Does David's last fix with additional interrupt checking in the Callable
> adapter do the trick? Something like this:
>
> public V compute(final A arg) throws InterruptedException {
> while (true) {
> Future<V> f = cache.get(arg);
> FutureTask<V> ft = null;
> if (f == null) {
> Callable<V> eval = new Callable<V>() {
> public V call() throws InterruptedException {
> V result = c.compute(arg);
> if (Thread.interrupted())
> throw new InterruptedException();
> return result;
> }
> };
> FutureTask<V> ft = new FutureTask<V>(eval);
> f = cache.putIfAbsent(arg, ft);
> if (f == null) {
> f = ft;
> ft.run();
> } else {
> ft = null;
> }
> }
> try {
> return f.get();
> } catch (CancellationException e) {
> throw new IllegalStateException("can't have been
> cancelled"); // ??
> } catch (ExecutionException e) {
> Throwable t = e.getCause();
> if (t instanceof InterruptedException) { // ft.run()
> was interrupted
> cache.remove(arg,f);
> if (ft != null) // we did ft.run() so we were
> interrupted
> throw (InterruptedException) t;
> // else retry
> } else {
> throw launderThrowable(t);
> }
> }
> }
> }
>
>
> Yuck. Makes that final example a lot harder to explain than we were
> hoping
> for.
>
> --tim
>
>
> On 10/18/06, Joe Bowbeer <joe.bowbeer@xxxxxxxxx> wrote:
> >
> > I think the existing cancellation exception handling would make more
> > sense if the task were submitted to an executor. Then a forced
> > shutdown of the executor, for example, could cause a cancellation
> > exception.
> >
> > Btw, when I've coded this kind of thing in the past, I've usually
> > finessed the problem by adding "throws Exception" to the method in
> > question :-)
> >
> > On 10/18/06, David Holmes <dcholmes@xxxxxxxxxxxxxxx> wrote:
> > > Alex,
> > >
> > > I have to concur with Tim. The intent was that interruption during
> > ft.run()
> > > implied cancellation and so there was a need to do clean-up of the
> cache
> > > entry. But there is nothing to convert the interruption to a
> cancel()
> > > request and so all that happens in the current case is that everyone
> who
> > > calls f.get() will get ExecutionException with a cause of
> > > InterruptedException.
> > >
> > > [...]
> > _______________________________________________
> > Concurrency-interest mailing list
> > Concurrency-interest@xxxxxxxxxxxxxxxxxxxx
> > http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
> >


<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise