JavaMemoryModel: Re: JMM problems with latch or copy-on-write?

From: Jeremy Manson (jmanson@cs.umd.edu)
Date: Fri Mar 30 2001 - 10:09:09 EST


> Specifically, transmission is guaranteed if the initializing thread
> subsequently releases a lock or writes to a volatile variable *and* the
> reading thread subsequently acquires the same lock or reads the same
> volatile variable.
>
> Example, in sequence:
>
> thread 1: initialized = true
> thread 1: synchronized(obj) { }
> thread 2: synchronized(obj) { }
> thread 2: assert(initialized == true)

Eep! That won't work, because in our model (again, can't speak for CRF),
useless synchronization can be optimized away. Plus, oh, your next point.

>
> Note, however, that the order of normal reads and writes is not preserved,
> so unless "initialized" is volatile, the reading thread may see "initialized
> == true" *before* it sees any other values assigned by the initializing
> thread. (The lack of preserved order makes your copy-on-write example even
> more vulnerable -- see #2 below.)
>
> Background synchronization can also guarantee the eventual transmission of
> unsynchronized "stop" signals in animation loops. Consider Jeremy's
> example:
>
> boolean stop = false;
>
> thread 1:
>
> while (!stop) {
> // do something
> }
>
> thread 2:
>
> stop = true;
>
> If thread 2 subsequently releases a lock or writes to a volatile variable,
> and "do something" in thread 1 acquires that same lock or reads that same
> volatile variable, then thread 1 must subsequently see "stop == true".

I am only begrudgingly admitting this is true, because it is not the
"nice" or the "clean" way of doing things. :) I suppose that if, for some
reason, you can use a volatile inside the loop and you can't use one for
the loop control variable, you can do this. But if you're doing that, why
not just make stop volatile?

>
>
> > (2) Copy on write. Again, I assume that another thread
> > will /eventually/ see the new reference to whatever was
> > replaced in a reasonably small amount of time even
> > though there is no explicit memory barrier.
>
> Once again: Declare the object reference volatile or have both threads
> access the object reference inside a synchronized block.
>
> Nevertheless, as with #1, the object reference and its contents can be
> transmitted by background synchronization. In this case, however, the side
> effects of reordering can be even more unexpected.
>
> Example:
>
> Foo foo = new Foo();
> Singleton.FOO = foo;
>
> The reference to foo may be transmitted by background synchronization, but,
> then again, the reading thread may see the reference to foo before it sees
> the initialized contents of foo.

I would add that *this means the idiom doesn't work with normal fields* so
don't do it.

>
> To guarantee that the reading thread will see the initialized contents (if
> it ever sees the reference), foo's fields must be declared "final" and/or
> its accessor methods must be synchronized.

Right. That was the point of re-jiggering final: so we could do that with
strings.

>
> In the following example, even if a thread sees the new "cell", it is not
> guaranteed to see "cell[0] == 1".
>
> int[] cell = { 1 };
> Singleton.CELL = cell;
>

also right.

                                        Jeremy
-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:31 EDT