[concurrency-interest] Extended access methods for Atomics (and AQS)
dl at cs.oswego.edu
Mon Apr 19 09:38:05 EDT 2010
On 04/15/10 08:49, David Holmes wrote:
> I just don't think it is a good idea to have these
> obscure, rarely usable methods sitting along side the methods that get used
> all the time.
I never know what to do about the immorality accusations
whenever trying to improve the current state of affairs
about ordering control for atomics. But here's another pass
at laying out the motivation and current status:
AtomicX already contains lazySet, which has a
naming bug (=> setInReleaseOrder) and specs in need
of improvement. But AtomicX doesn't contain the
complementary weaker-than-volatile form of get
(=> getInRelaxedOrder) that is sometimes used with
it (as mentioned by Hans). My draft proposal fixes these,
and further regularizes API to accommodate a long-standing
RFE to add corresponding forms of CAS. The net result
has pretty much the same structure as C++0x atomics, which
is not only a good sanity check, but also in the future
(as C++0x atomics become supported by C++ compilers) will
improve confidence that JVMs written in C++, runtime-systems
supporting both C++ and Java, etc, work consistently.
If we had not already previously included some methods for
non-volatile ordering control (i.e., weakCompareAndSet, lazySet)
in AtomicX, I might be more sympathetic to creating classes
AtomicXWithExtendOrderingControl. But as it stands, I don't think
the mere act of further fleshing them out and regularizing them
is a good reason to do so.
I illustrated API with AtomicInteger, but as always,
the main practical need is for ordering methods applying
to the fields of other objects (including array elements).
These are more typical/desirable because without them:
(1) if you need ordering control and must create
another Atomic object to obtain it, then you have traded one
problem for two problems, because you must now figure out
how to correctly maintain reference to that Atomic;
(2) Creating AtomicXs (especially cases like arrays of
AtomicIntegers as opposed to arrays of ints supporting ordering
control) can (and usually does) lead to so much space and
indirection bloat that you may be better off just running slow
lock-based code (assuming you can figure out how to use locks in
such cases, which is not always possible).
If Java/JVMs supported some way to force object-inlining
so that a (suitably declared) AtomicInteger field of
another object could be embedded as an int field but
accessed via atomics, there would be no need for alternatives.
But I don't think this will happen anytime soon.
Short of such major language/JVM changes, one way of handling
these cases is with Fences, that separate out the ordering
control from the reads and writes (although unfortunately not CAS).
This way, programmers just do the field accesses but surround
them with acquire-, release-, or volatile- style fence method
calls (which would normally be instrinsified and inlined).
But many people objected to this approach: Some on
morality grounds, others on usability grounds, and others
because the specs under this form of separation are very
hard to nail down and incorporate into JMM/JLS.
It seems that most people agree that it would be better
all around to support and spec ordered accesses
than separating accesses plus orderings.
One way to do this is to extend AtomicXFieldUpdaters.
This is not a very nice choice. Java, Java bytes codes, and
JVMs were not originally designed to support "l-value"
operations on fields of objects beyond simple read/write.
The only path to do so is via the defacto-standard "Unsafe"
APIs that bypass all of the normal apparatus (including
verification and safety checks) to perform ordered/atomic
accesses at particular offsets of objects/arrays. So if
you want to export these methods in public APIs, you must surround
them with Java-level dynamic type- and accessibility-
checking (and further, restrict use to fields marked as
volatile, which doesn't work for arrays).
Which brings us to the same kind of cure-worse-than-disease
problem seen with the alternative of using standalone Atomics:
On most platforms, the overhead of performing, say
updater.lazySet(x) (aka setInReleaseOrder(x)) is greater than
any savings you get from avoiding underlying hardware fences
if you simply did a direct volatile write. (Worse, in some
cases the checks may themselves entail stronger fences.)
Even in their current forms (without the above minor extensions)
no one is tempted to use these methods. (Google code search
does not find even one application-level usage of
AtomicIntegerFieldupdater.lazySet.) Instead, as
is occasionally posted on this list, people developing carefully
weakly ordered code have learned to use Unsafe directly,
avoiding the overhead. Although at the price of having no
language-level static checking and containing code that does
not work on systems with security managers.
My hope has been that there might be some way to reduce
this dynamic checking overhead in updaters, so that people
can stop using Unsafe directly. But after some further exploration,
I'm less optimistic about the prospects -- even with JSR292-based
support, the cases in which you can trade improved updater
creation-time checks for fewer or faster dynamic per-update
checks don't seem to cover many usages.
Which leaves me not having a really good plan at the moment.
This is a problem that is not going to go away all by itself,
so concrete suggestions would be welcome.
More information about the Concurrency-interest