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

Doug Lea dl at cs.oswego.edu
Sat Apr 10 09:47:42 EDT 2010


On 04/08/10 12:46, Chris Povirk wrote:
> My goal is a family of operations like these:
>
> // Neither of these throws InterruptedException.
> V result = Futures.getUninterruptibly(future);
> CountDownLatches.awaitUninterruptibly(latch);
>

Here are some more notes on this, with some embedded
concrete proposals. My sense is that it might be a bad idea
to uniformly paper these over via layered libraries, but
instead to better deal with at least some of them in j.u.c proper.

Backing up: You can (very!) roughly divide concurrency
into two categories:
  - latency-driven: "reactive", typically "asynchronous"  techniques
      dealing with intrinsic concurrency, as in servers with multiple
      clients etc
  - throughput-driven: "parallel" techniques that speed up programs
      via multiple CPUS/cores/threads.

There are a lot of in-betweens that trade off some aspects of
responsiveness/latency vs throughput. But it is still a useful
way to think about the  issues at hand.

Most (but not all) initial j.u.c components were oriented
toward reactive concurrency. Over time we've added more support
for throughput-driven parallel programming, in response to the
increasing availability of multicores and MPs.
The main addition is of course the ForkJoin framework,
that is so much geared toward aggregate throughput
via structured parallel computations that it does not even
support use of some asynchronous techniques like blocking on
queues.

As mentioned in my last post, most aspects of throughput-driven
frameworks and components don't hugely differ from others,
but one place they do is dealing with interrupts. Here, interrupts
per se are not typically useful to deal with by users, but they
still may play some internal role in supporting cancellation.
Cancellation is, often enough, an ingredient of parallel
computation control. You'd like responsiveness to events that
allow you not to compute things -- for example cancelling some
subtasks of a parallel search if any one of them succeeds --
thus improving throughput for other parts of a program.
Also, interrupts may play a role in shutting down an entire
parallel component/framework, as a more extreme form of
cancellation.

These and related thoughts led to defining ForkJoinTask
as an abstract base class extension of Future.
Among other things, ForkJoinTask introduces method "join"
which is similar to the proposed "Futures.getUninterruptibly(future)".
It would be possible to do this more generally by introducing
interface JoinableFuture between Future and ForkJoinTask, so
people could use this outside of FJ. I'm not positive that this is a
great idea in the long run, but it may be helpful for
those who use a more throughput-oriented approach while still
using some of the other more reactive-oriented j.u.c components,
and would also make it easier to mix and match Futures and FJ.
Comments and suggestions about this would be welcome.
As Chris mentioned, doing this would also lead to introducing
a stand-alone class like JoinableFutureTask or maybe a factory
that could create instances of a default implementation.

A similar but easier story applies to Phaser, that
generalizes both CountDownLatch and CyclicBarrier.
Phaser.arriveAndAwaitAdvance can and should be used instead of
layering CountDownLatches.awaitUninterruptibly(latch).

Also, several of the other AQS-based synchronizers already
support uninterruptible waits. And all of the various non-blocking
data structures are of course unaffected.

The main classes that don't fit into this well are the
various BlockingQueues (and extensions thereof). It is not
completely clear to me that they should. The main role
that they play in throughput-driven programming is under the
hood, in mechanics that coordinate handling tasks or data. But
these mechanics often need to deal with interrupts anyway
in support of various forms of cancellation/shutdown. However,
if a good case can be made for it, we do have the new
TransferQueue interface available to exploit to add some
associated methods, that could at least be used in a
correspondingly updated LinkedTransferQueue class. Does
anyone have a good story about why this might be worth doing?

-Doug






More information about the Concurrency-interest mailing list