[concurrency-interest] InterruptedException-free wrappers for calls that "will never be interrupted"

Chris Povirk cpovirk at google.com
Thu Apr 8 23:36:21 EDT 2010


Joe,

Thanks for the response.

You're right that the subject line I chose has artificially narrowed
the discussion to the "never interrupted" case.  In the end, the
question I'm interested in is "What should a Future.get that doesn't
throw InterruptedException look like?"  It's a truism that a
loop-and-retry implementation is the right implementation when you
want loop-and-retry, as you point out, so I didn't want to dwell on
this point.  But the result was an e-mail that focused too much on the
"never interrupted" case.  I believe that there is value in supporting
both use cases, and, as I'll now argue, I believe that loop-and-retry
can do that.

We agree about the unique advantage of loop-and-retry.  I disagree,
however, that that strategy is more specialized than the
RuntimeException strategy.  I see loop-and-retry as the better
approach for calls that "will never be interrupted."

The reason is the same as the reason that I put scare quotes around
"will never be interrupted."  I think we can agree that, if an
operation truly is never interrupted, any reaction to
InterruptedException is equally effective -- retry, wrap, ignore,
System.exit, Runtime.exec("rm -rf /")....  The question is what to do
when the "impossible" interrupt occurs.  I believe (based on evidence
from Google and non-Google code) that we as programmers are much too
quick to assume that an interrupt is impossible.
(executor.submit(task).cancel(true) would catch a lot of our libraries
by surprise.)

You've pointed out the responsiveness advantage of handling interrupts
by throwing a RuntimeException.  This is an advantage, but I don't see
it as a large one.  Uninterruptible tasks already exist --
uninterruptible IO, heavy math computations, or callers of
Semaphore.acquireUninterruptibly, for example.  A task that does not
exit when interrupted can be a problem, but it's a problem that many
systems must already cope with.

More important is the disadvantage of throwing a RuntimeException: it
encourages the programmer to believe that the operation will always
complete even though the implementation makes no such guarantee.  If
the programmer believed that the exception were possible, he would
have caught or propagated the original InterruptedException; there
would be no need for an uninterruptible wrapper.  We've made it easier
for him to believe that queue.putUninterruptibly(task) "can't fail,"
but if it does, his program drops a task on the floor.

Additionally, I believe there's less value in a library implementation
of the RuntimeException-throwing variant because it's comparatively
easy for the programmer to write himself (especially if the
InterruptedRuntimeException constructor restores the interrupt for
him):

try {
  ... perform any number of interruptible operations ...
} catch (InterruptedException e) {
  throw new InterruptedRuntimeException(e);
}

As opposed to:

... maybe declare a field outside the finally block ...
boolean interrupted = false;
try {
  long timeoutNanos = timeoutUnit.toNanos(timeoutDuration);
  long end = System.nanoTime() + timeoutNanos;
  while (true) {
    try {
      ... perform one number of interruptible operations ...
    } catch (InterruptedException e) {
      timeoutNanos = end - System.nanoTime();
      interrupted = true;
    }
  }
} finally {
  if (interrupted) {
    Thread.currentThread().interrupt();
  }
}
... repeat for each other interruptible operation...


Jim: Thanks, we've considered throwing everything into an
Uninterruptibles class.  This solves a couple of the problems that
arise from splitting the methods across classes named Futures,
BlockingQueues, etc.


More information about the Concurrency-interest mailing list