[concurrency-interest] InterruptedException-free wrappers for calls that "will never be interrupted"
cpovirk at google.com
Thu Apr 8 12:46:01 EDT 2010
My goal is a family of operations like these:
// Neither of these throws InterruptedException.
V result = Futures.getUninterruptibly(future);
These methods would retry the operation until it completes without
interruption and then restore the interrupt. (Another option is to
wrap the InterruptedException in a custom InterruptedRuntimeException
class; I'd be happy to hear people's opinions on this, as well.)
Google has implementations of some of these operations, and we're
planning to expand our coverage and release them in our open-source
Guava project. However, we're uncertain about a few issues.
Below I've listed the two main decisions we need to make along with a
number of pros and cons for each of their options.
Decision A: The interface to these uninterruptible methods could take
one of three forms:
1. Static Futures.getUninterruptibly methods.
2. A static uninterruptibleFuture method that returns an
UninterruptibleFuture, which adds getUninterruptibly methods to
3. A static uninterruptibleFuture method that returns an
UninterruptibleFuture, which overrides Future's get methods to no
longer throw InterruptedException.
(1) requires the fewest method declarations.
The method declarations from (1) are likely to be jumbled together
with unrelated declarations. A person who sees
offerUninterruptibly, and pollUninterruptibly may get the impression
that the BlockingQueues contains only InterruptedException-removing
methods, when perhaps it contains methods like takeMultiple(Queue<E>,
int). Plus, subclassing is awkward: does BlockingDeques provide a
static putUninterruptibly method, too? (An alternative is to put
methods for all interfaces in a single Uninterruptibles class.)
(1) is the best solution for classes with final methods (notably
Thread.join). Even (2) suffers from this problem:
joinUninterruptibly() would join the delegate thread, as desired, but
join() (which we can't override) would join the unstarted wrapper
thread. This is confusing. Luckily, Thread.join and other primitives
are used less frequently than they used to be, especially among the
kind of people that I hope will use the uninterruptible wrappers.
(Related: Developers who want to make their methods final can define
interfaces so that we don't need to override the class's methods.)
Declaring a variable or field to be an UninterruptibleFuture (possible
in (2) or (3)) can express our intention to use it that way to readers
and make the uninterruptible methods available in IDE autocompletion.
Methods can declare a return type of UninterruptibleFuture, making the
uninterruptible operations more convenient and bringing them to the
attention of more users.
(3) hides the original, interruptible methods. This is especially a
problem if a method has returned an UninterruptibleFuture to the user
for "convenience," as proposed above.
(2) is most consistent with the JDK's existing uninterruptible methods.
In principle, a Future implementation could implement
UninterruptibleFuture and some other Future subinterface (e.g., one
whose get methods do not throw ExecutionException). In practice, this
"UninterruptibleFuture" is a poor name. The name may suggest to some
that a *task* whose value the Future awaits may not be interrupted
through Future.cancel. But "Uninterruptible" refers only to the get
methods; it's more like "UninterruptiblyGettableFuture," which is an
"UninterruptibleFuture" is a particularly poor name in the case of
(2), since its get methods would still be interruptible. (This is
another problem that "UninterruptiblyGettableFuture" could solve.)
Decision B: There are two ways to handle the retry boilerplate:
1. Repeat it everywhere it's needed.
2. Delegate to a performUninterruptibly method that takes an
(A colleague proposed a reflection-based alternative, but we found it
to have two problems: First, CountDownLatch isn't an interface, so we
can't extend it to create an UninterruptibleCountDownLatch interface
to pass to Proxy. Second, we can't handle timeouts without either
annotating the long+TimeUnit parameters or assuming that there's a
convention for them.)
(1) has the usual advantages and disadvantages of copy-and-paste programming.
If there's a significant performance difference, (1) is surely the winner.
(2) requires three variants of InterruptibleOperation: one for
operations without timeout, one for operations that time out by
returning a sentinel value, and one for operations that time out by
throwing TimeoutException. (We might be able to combine the last two
with suitable generics, but my claim of "three variants" already
presumes some awkward parameterization to handle exceptions.)
I'm currently leaning toward 2-1
(UninterruptibleFuture.getUninterruptibly, repeat the boilerplate),
but some smart people disagree. Opinions?
More information about the Concurrency-interest