At the risk of sounding naive, I'll admit to being confused about what
we're discussing here. Bill's original description was:
Provide a synchronization idiom that provides a safe way for a thread
to construct an object, perform a series of updates on the object, and
then publish the object (store a reference to the object in memory
read by other threads). Other threads should then be able to read that
reference and see an object that reflects all of the modifications.
Importantly, the threads reading that reference should be able to do
so without any synchronization.
Once again, this guarantee only applies to seeing a consistent and
complete object. Other parts of the world state may be inconsistent.
I *think* this example captures the pattern Bill was talking about:
class Publisher {
public static Foo unchangingFoo = null;
... { // code that runs once, in one thread
Foo foo = new Foo(arg, arg, arg);
foo.someMethod(more, args);
foo.someField = aValue;
// publish the object
unchangingFoo = foo;
}
}
class User {
... { // code that runs in many threads
Foo foo = Publisher.unchangingFoo;
if (foo != null) {
// The object named by foo should be correctly formed
// and usable.
}
}
}
Now, my understanding is that this code is broken for MP machines with
relaxed memory order, because the writes to the foo object by the
constructor, someMethod, or the putfield may not be seen by a processor
running the User code.
This seems all well and good. There was, I thought, a relatively simple
solution to this problem, which required changing the Publisher code to:
// publish the object
synchronized (foo) {
unchangingFoo = foo;
}
or, perhaps,
// publish the object
synchronized (foo) { }
unchangingFoo = foo;
and that would force a memory barrier between creating the object and
publishing it to the world.
But then I read Bill's note, which included this set of possible
actions:
Processor 2 has cached the
contents of address 0x1234
Processor 1 allocates a new
object at address 0x1234
Processor 1 initializes the
object at address 0x1234
Processor 1 does a memory
barrier
Processor 1 stores 0x1234
into address 0x5678
Processor 2 reads address 0x5678,
gets 0x1234
Processor 2 reads contents of object at
address 0x1234 out of stale cache line
This does, unfortunately, make sense. I had assumed that a memory
barrier would (somehow) inform other processors that their cached data
might be stale. But I can believe that there are NUMA architectures
where the stale read could occur.
I am left asking ``Can one actually implement Java, with the usual safety
guarantees, on such an architecture?'' That is, is it possible to ensure
that object headers are initialized properly before objects are seen in
other threads?
(Blue skying for a second, I guess it's possible to envision a GC design
where all processors ensure that newly allocated objects are not cached
in any stale lines. For that to work, it probably prohibits allocation
of two objects from the same cache line.)
Right now, it appears to me that the only feasible answer is memory
barriers around almost all reads. Which scares me a lot, because I have
a customer who wants to ship my technology on SMP Alpha hardware in the
near future, and (1) we haven't done the work to support this yet and
(2) the performance would be much worse than uniprocessor.
Now, please, tell me where I'm wrong.
--p
-------------------------------
This is the JavaMemoryModel mailing list, managed by Majordomo 1.94.4.
To send a message to the list, email JavaMemoryModel@cs.umd.edu
To send a request to the list, email majordomo@cs.umd.edu and put
your request in the body of the message (use the request "help" for help).
For more information, visit http://www.cs.umd.edu/~pugh/java/memoryModel
This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:13 EDT