[concurrency-interest] AtomicReferenceFieldUpdater vs Unsafe
hans.boehm at hp.com
Mon Nov 28 15:27:40 EST 2011
> From: David M. Lloyd
> Sent: Monday, November 28, 2011 11:49 AM
> To: concurrency-interest at cs.oswego.edu
> Subject: Re: [concurrency-interest] AtomicReferenceFieldUpdater vs Unsafe
> On 11/28/2011 01:23 PM, Boehm, Hans wrote:
> > There's another problem with tryLock()-like methods, which I pointed out
> in a 2007 PPoPP paper. I think the current j.u.c.tryLock() is not correctly
> specified. The problem is illustrated by the following badly designed code:
> > Thread 1:
> > x = 17;
> > l.lock();
> > Thread 2:
> > while (l.tryLock()) l.unlock();
> > ... x ... // x should be 17 here!
> > Effectively this inverts the sense of the lock. Thread 2 can only proceed
> after thread 1 acquires the lock.
> > The problem is that:
> > - There are no potentially concurrent accesses to x in a sequentially
> > consistent execution of this code, and hence there should be no data
> > races
> > - The Java memory model does not in fact guarantee that x is 17 in thread 2,
> because there is a data race by Java memory model definitions. This race is
> too hard to explain.
> > - In order to ensure that x is actually 17, we would need to prevent
> reordering of the two lines of thread 1. This is expensive on something like
> PowerPC, and doesn't benefit well-written code.
> I'd say that an even more basic problem is that if thread 1 locked and
> unlocked quickly enough, and the scheduling fell out just right, thread
> 2 would never even notice that thread 1 took the lock if it was unscheduled
> after its unlock and not rescheduled until after thread 1 unlocked again.
> I'm not sure this is a weakness in tryLock(). The user is making all kinds of
> faulty assumptions in this code.
I'm assuming that thread 1 never unlocks the lock l. I agree this is not code you should write. But the current specification suggests it should work. It shouldn't.
Our experience with C++ is that this is not as esoteric a point as I once believed. My initial hypothesis is that nobody would really write this kind of code. However, once we allowed spurious failures for tryLock(), we got pushback from several sources, and they all came with real examples that either would actually fail with what I presume to be the standard PowerPC implementation, or were prone to subsequent innocuous-looking changes that would have subjected them to such failures. They all came from experts, and looked much less ridiculous than the toy example above, though I still believe they all should have used atomics instead. But I'm convinced that it's important to get this part of the spec right.
And this is a lousy reason to have to explain to beginners that Java's notion of data race is ALMOST what you would expect, but there are these weird cases in which operations that can't execute concurrently can race, and if you're going to use tryLock you really need to understand this happens-before stuff instead.
More information about the Concurrency-interest