[concurrency-interest] Fences, AtomicXFieldUpdater enhancements, and @Racy annotations

Doug Lea dl at cs.oswego.edu
Fri Jan 16 08:43:12 EST 2009


[This one is equally on java.util.concurrent and JMM issues,
so please be tolerant of cross-post duplication.]

First, I'm getting more confident that the specs for Fence
API are in the right ballpark, although still need a bit
more precision in the discussion of "scopes".  (If anyone
want to help, please do!) See the updated draft
http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/atomic/Fences.html

Second, I am continuing my crusade to rid the world of
explicit use of "sun.misc.Unsafe". And these plans might
mesh well with the usage concerns that Bill Pugh and I have
discussed and that he's posted about (as in: "yow" :-)

Here's the story.

As mentioned in my initial posting on Fences (1) everyone would
be happier if they almost never had to use them directly, but
instead could get the effects via methods with recognizable
semantics (2) people can get some of these effects already by
using AtomicXFieldUpdaters, but even in those cases often don't.

We can address these, and make AtomicXFieldUpdaters
(1) Applicable in more contexts
(2) Cheaper in most cases where performance matters.
(3) Better advertise intent

Background. For those who have never used them,
AtomicXFieldUpdaters are ugly to declare, but provide
operations like compareAndSet to (volatile) fields of
objects directly, without needing to create stand-alone
AtomicX objects. (If you don't do a lot of low/medium-level
parallel/concurrent programming, you might not think this is
a big deal.  But it is: footprint, locality, and indirection
hurt a lot more here than in sequential programs.)

Here's a sample usage (pasted from ConcurrentSkipListMap)

public class ConcurrentSkipListMap<K,V> ....  {
     /**
      * The topmost head index of the skiplist.
      */
     private transient volatile HeadIndex<K,V> head;

     private static final
         AtomicReferenceFieldUpdater<ConcurrentSkipListMap, HeadIndex>
         headUpdater = AtomicReferenceFieldUpdater.newUpdater
         (ConcurrentSkipListMap.class, HeadIndex.class, "head");

     /**
      * compareAndSet head node
      */
     private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
         return headUpdater.compareAndSet(this, cmp, val);
     }

     // ...
}

We can't do much about the 4 lines it takes to declare
one of these (maybe IDEs could help), but we can address
the other problems.

1. Less restrictive API

Right now, we require that the target field be "volatile",
and don't support any non-volatile, "relaxed" operations
through it. As we've been discussing wrt Fences, there are
use cases where these arise. We can support them by lifting
volatile field declaration restriction and/or supplying two
additional methods (illustrated with
AtomicIntegerFieldUpdater).

     /**
      * Gets the value held in the field of the given object managed
      * by this updater, using non-volatile memory consistency rules.
      *
      * @param obj An object whose field to get
      * @return the current value
      */
     public abstract int relaxedGet(T obj);

     /**
      * Sets the field of the given object managed by this updater to the
      * given updated value using non-volatile memory consistency rules.
      *
      * @param obj An object whose field to set
      * @param newValue the new value
      */
     public abstract void relaxedSet(T obj, int newValue);

(Additionally, we need to include AtomicXArrayUpdater
classes, that differ from AtomicXArrays in that you hand an
existing array to constructor/factory-method. We had been
resisting this for similar reasons.)


2. Better typical performance

First, more background:

When people use Unsafe rather than updaters, it is often
because they have discovered that there is non-trivial
dynamic check overhead when using updater methods -- this
overhead dwarfs that of some of the simpler operations like
get(). The reason these checks exist is that even though all
usages are statically typesafe with respect to a Java
compiler, we must protect against people writing raw
bytecode that uses the underlying intrinsics to access
inaccessbile data etc. Such code can otherwise make it
through class loading checks etc, so must be screened.

The now common "try to use Unsafe anyway" hacks (like those
near the bottom of the preliminary releases of jsr166y
classes like ForkJoinPool), exist solely to bypass this: If
the class/caller is allowed to use Unsafe because it is in
bootclasspath or the program has no SecurityManager, or one
that allows access to Unsafe, then do so, else abort.

The improvement to updaters is to automate and extend this
hack: If you are allowed to access Unsafe, provide an
updater that bypasses checks, else the slower kind (rather
than aborting). Luckily we use static factories here so this
part is straightforward. This is a little tricky to set up
but seems doable, subject to review of mechanics by security
folks.

The net result is that updaters will have performance that
should be identical to raw Unsafe calls in usages that do
not require security checks, which is very likely to include
most performance-sensitive applications.  Those that do need
checks will be slower, but for good reason.

3. Better advertised use

The above changes will make updaters a lot more useful and
probably a lot more used.  In fact, there are reasonable
ways to use them to get almost all the effects of Fences
(not quite all though).

But with power comes responsibility.
One downside of updaters is that the declaration of
an updater is removed from the declaration of the field
it is updating, which makes usage hard to check.

This is where annotations can help: Any field that
will be used via an updater should have some annotation
saying so. One good candidate name for this annotation is
   @Racy
The reasoning is that any such variable is allowed
to be used in ways that "don't count" with respect
to the usual definitions of race-freedom.

I don't think it is essential that @Racy be a required
annotation that is checked by javac, but it is essential
for extended checkers, which could help enforce the
typically (but probably not universally) desirable
requirement that if a variable is declared as
@Racy, then ALL reads and writes of it must go through updaters.

It's further possible to make @Racy take some sort of
argument expressing intent, like @Racy("Publish").  But if
this goes anywhere, these issues are probably best left to
JSR305.

Aside to Paul McKenney and other foreigners.  Notice that,
under this scheme, @Racy is nearly synonymous with C++0x "atomic"
but has a much more accurate name :-)

-Doug




More information about the Concurrency-interest mailing list