views:

83

answers:

2

In code listing 5.19 of the Brian Goetz book Concurrency In Practice, he presents his finished thread safe Memoizer class.

I thought I understood the code in this example, except that I don't understand what the

while ( true )

is for at the start of the

public V compute(final A arg) throws InterruptedException

method.

Why does the code need the while loop?

Here is the entire code sample

public class Memoizer<A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
        = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

    public Memoizer(Computable<A, V> c) { this.c = c; }

    public V compute(final A arg) throws InterruptedException {
        while (true) {
            Future<V> f = cache.get(arg);
            if (f == null) {
                Callable<V> eval = new Callable<V>() {
                    public V call() throws InterruptedException {
                        return c.compute(arg);
                    }
                };
                FutureTask<V> ft = new FutureTask<V>(eval);
                f = cache.putIfAbsent(arg, ft);
                if (f == null) { f = ft; ft.run(); }
            }
            try {
                return f.get();
            } catch (CancellationException e) {
                cache.remove(arg, f);
            } catch (ExecutionException e) {
                throw launderThrowable(e.getCause());
            }
        }
    }
}
+1  A: 

It looks as if the main objective of the code is to compute whatever type A is. It seems the only way the while(true) can ever be effective is if there is a cancellation. If there is a cancellation then the method will retry the compute.

Basically, while(true) will ensure that (other then an ExecutionException), at some point, the function will complete and compute correctly even with a cancellation.

John V.
+4  A: 

Eternal loop retries on CancellationException. If any other exception is being thrown the execution will be stopped.

Biotext dot org has a blog entry on the same issue.

Boris Pavlović
Yes. And it is clear to me know I have re-read it. Thanks very much for the excellent link too.
russelldb
But in the sample, the FutureTask never gets cancelled. Can you help me understand how that code path might be executed?
jabley
FutureTask is being created and added to the cache. Some other process could get the task from the cache and cancel it.
Boris Pavlović
Assuming that the code in the listing in the book is complete then that should not be possible. The cache is encapsulated. So I'm confused as to the actual source of a CancellationException in the code presented, (though I still understand and accept your original answer as to the intention and purpose of the while loop, I share jabley's concern about it inclusion in this instance.)
russelldb
c.compute could potentially throw the unchecked CancellationException. The implementation of the Computable could potentially be more complex; e.g. use FutureTask itself; and reasonably throw CancellationException. From my hazy recollection of Doug Lea's Concurrent Programming in Java, that sort of exception should generally be propagated. The Memoizer code is generic; for the given example, that would never happen. But if you were to re-use Memoizer in another context, handling the CancellationException would be reasonable.
jabley
OK. Good answer. And I am at peace, finally.
russelldb