Win a copy of Cross-Platform Desktop Applications: Using Node, Electron, and NW.js this week in the JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

read-write reordering question  RSS feed

 
Yuriy Zilbergleyt
Ranch Hand
Posts: 429
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

Could the println() statement in this class ever attempt to print out an incompletely constructed string, since the instance variable s isn't volatile? This is assuming that another thread calls setS() while an instance of Test is running.



Thank you,
Yuriy
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Yuriy Zilbergleyt:
Hello,

Could the println() statement in this class ever attempt to print out an incompletely constructed string, since the instance variable s isn't volatile? This is assuming that another thread calls setS() while an instance of Test is running.

Thank you,
Yuriy


No... it is not possible to print out an "incompletely constructed" string. However, since the variable is not volatile. It may print out an old copy of the string that it had cached.

Henry
 
Yuriy Zilbergleyt
Ranch Hand
Posts: 429
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the reply, Henry!

What makes this case different from the non-volatile double-checked locking inside a Singleton? I thought that in the singleton, the getInstance() method could return a partially constructed object because it failed the "if (instance == null)" test.

Thank you,
Yuriy
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Yuriy Zilbergleyt:
Thanks for the reply, Henry!

What makes this case different from the non-volatile double-checked locking inside a Singleton? I thought that in the singleton, the getInstance() method could return a partially constructed object because it failed the "if (instance == null)" test.

Thank you,
Yuriy


Not sure what are the particulars you are referring to... but quite simply, the first time that this class sees any string object, is during the set method. And by then the object has been created.

Henry
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Henry Wong:


Not sure what are the particulars you are referring to... but quite simply, the first time that this class sees any string object, is during the set method. And by then the object has been created.

Henry


I'm sure he's talking about this (in regards to double-checked locking):

The most obvious reason is that the writes which initialize instance and the write to the instance field can be reordered by the compiler or the cache, which would have the effect of returning what appears to be a partially constructed Something. The result would be that we read an uninitialized object.


Which has nothing to do with the code posted. The writes don't have anything to do with printing. The problem, amongst other things, was that the instance field could be written before the Object was initialized.
 
Yuriy Zilbergleyt
Ranch Hand
Posts: 429
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The problem, amongst other things, was that the instance field could be written before the Object was initialized.


Yes, that's what I was talking about. Suppose a different thread calls test.setS(new String("blah")), can the call be reordered so that setS() is executed before the new String's constructor? Is there a chance that when System.out.println(s) is called, the variable is uninitialized, so the value printed will be neither the old value from the cache nor the new value?

Thank you,
Yuriy
[ January 20, 2006: Message edited by: Yuriy Zilbergleyt ]
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator


It's possible for a thread to get back a partially initialized instance under the old memory model, correct? What happens if that thread then invokes print() on that partially initialized instance? Could a partially initialized String get passed to System.out.println()? I don't know but I'm sure Mr. Wong does.
[ January 20, 2006: Message edited by: Ken Blair ]
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Yuriy Zilbergleyt:

Yes, that's what I was talking about. Suppose a different thread calls test.setS(new String("blah")), can the call be reordered so that setS() is executed before the new String's constructor? Is there a chance that when System.out.println(s) is called, the variable is uninitialized, so the value printed will be neither the old value from the cache nor the new value?

Thank you,
Yuriy


No... The construction of the object is completed before it is used in the method. It is the same thread that will construct that object that calls the method, I think it is safe to assume that the compiler will get it right...

The act of creating objects at the moment of calling a method is such a common practice that too many things will break otherwise.

Henry
[ January 20, 2006: Message edited by: Henry Wong ]
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:

It's possible for a thread to get back a partially initialized instance under the old memory model, correct? What happens if that thread then invokes print() on that partially initialized instance? Could a partially initialized String get passed to System.out.println()? I don't know but I'm sure Mr. Wong does.


I keep getting told that it is possible to have a partially created object, but quite frankly, I have never seen any affect from it. And as a consultant, I can't even begin to count how many times I have encountered the DCL pattern (including code that is generated by a couple of commercial packages).

Maybe it does happen... I am just saying that I have never seen any affect from it.

Henry
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Henry Wong:
No... The construction of the object is completed before it is used in the method. It is the same thread that will construct that object that calls the method, I think it is safe to assume that the compiler will get it right...
[/QB]


Yes, for the thread that calls the setter, the object will be fully created. But the other thread may see it differently - for example, it is *allowed* to use the uptodate reference to the String, but it's own cached values for the memory where the String itself resides. That way, that thread *could* see a partially initiallized String. Unless you synchronize, of course.

That's true until 1.4, at least. I'm not sure in which ways this changed for Tiger.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Henry Wong:
I keep getting told that it is possible to have a partially created object, but quite frankly, I have never seen any affect from it.


I believe you that you've never routed a bug back to this cause - because that would be rather hard to do. But how do you know that none of oddities you occasionally see was ever cause by this effect?

And as a consultant, I can't even begin to count how many times I have encountered the DCL pattern (including code that is generated by a couple of commercial packages).


And that code was totally bug free, I guess? :roll:

Seriously, even the source code of the JDK itself contains a lot of questionable constructs. That doesn't mean that they are OK.


Maybe it does happen... I am just saying that I have never seen any affect from it.


That could also have to do with the systems you work on. It's much more likely to happen with systems that make heavy use of of multi-threading on multi-processor machines. The JVM you use also has a high impact, I guess.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
BTW, I've never noticed the effect myself, either. But again, I wouldn't know how to easily identify it as such at all.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Henry Wong:


I keep getting told that it is possible to have a partially created object, but quite frankly, I have never seen any affect from it. And as a consultant, I can't even begin to count how many times I have encountered the DCL pattern (including code that is generated by a couple of commercial packages).

Maybe it does happen... I am just saying that I have never seen any affect from it.

Henry


I haven't either, but then again none of my code uses DCL and I haven't seen any co-workers code that did either. I was just trying to elaborate on what the OP seemed to be asking about. If it's true that the compiler can reorder the writes then I should think it would be true that it could get an partially initialized instance which may then have a partially initialized String. Even if it is possible I would think it would be very difficult to produce even purposefully.

I may try and produce it for fun but I'm not even which compiler(s) may reorder the writes to begin with.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:

I may try and produce it for fun but I'm not even which compiler(s) may reorder the writes to begin with.


I don't even think it's the compiler which does this, but the processor. Which would, of course, very heavily depend on what the hot spot engine decides to do.

Also keep in mind that it's not only reordering which is the problem - it's also caching. Each thread has it's own copy of registers (on platforms that work that way). No imagine a thread creating a new object and putting part of it into main memory, and caching an int field in a register for performance reasons. It's not hard to imagine a different thread (which accesses the same main memory, but different registers) to see the object in an inconsistent state (say, with the wrong hashcode).
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ilja Preuss:

Yes, for the thread that calls the setter, the object will be fully created. But the other thread may see it differently - for example, it is *allowed* to use the uptodate reference to the String, but it's own cached values for the memory where the String itself resides. That way, that thread *could* see a partially initiallized String. Unless you synchronize, of course.

That's true until 1.4, at least. I'm not sure in which ways this changed for Tiger.


That is exactly the point, the object will be fully created, before a reference is made and placed on the stack for the setter. The other thread doesn't even have a reference to the object until sometime in the setter -- definitely after the object is fully constructed.

Henry
 
Henry Wong
author
Sheriff
Posts: 23275
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:

I haven't either, but then again none of my code uses DCL and I haven't seen any co-workers code that did either. I was just trying to elaborate on what the OP seemed to be asking about. If it's true that the compiler can reorder the writes then I should think it would be true that it could get an partially initialized instance which may then have a partially initialized String. Even if it is possible I would think it would be very difficult to produce even purposefully.

I may try and produce it for fun but I'm not even which compiler(s) may reorder the writes to begin with.


Ilja, Ken,

I am not trying to belittle the problem. I am merely trying to answer the question by giving a brain dump of my encounters with the DCL pattern -- which obviously, is not enough to see any affect from it.

Henry
[ January 21, 2006: Message edited by: Henry Wong ]
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Henry Wong:

That is exactly the point, the object will be fully created, before a reference is made and placed on the stack for the setter. The other thread doesn't even have a reference to the object until sometime in the setter -- definitely after the object is fully constructed.


Yes, the object *is* fully constructed - in the memory of thread 1. But until thread 1 has passed a write barrier, there is no guarantee that it is fully written to the main memory; and until thread 2 has passed a read barrier, there is no guarantee that it has fully updated its local memory. So it's quite possible for thread 2 to see an only partially created object.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I agree with Ilja here. But I must also concede that I've never been able to clearly observe this effect. I believe this problem exists on two levels: specification and implementation. The latter can be particularly hard to observe problems in, as the effects we're talking about may occur very sporadically, or not at all in many implementation. However I have heard that they have been observed in actual real live JVM implementations. The clearest statement of this I can find at the moment is here: "several JVMs have exhibited this behavior". Unfortunately I can't find details about which JVMs exactly. Still, I'm willing to believe it's occurred.

So, were those JVMs just buggy? That takes us to the specification level, where at least we all have access to the same JLS/JVM specs. Unfortunately the problem is that the old specs seem to have omitted rules which prohibit a JVM from reordering instructions in a way that forbids the visibility of partially constructed objects in other threads. This is hard to point at though - there's not a single section we can point at and say, see, it's possible. Rather one would have to read all the specs, understand them, and verify that there was nothing there to prevent the reordering. Which is a tall order. I've read some of the specs, the sections that seem most relevant, and I partly understand them, but that understanding is definitely incomplete and quite possibly in error. So I end up relying more on things like the JSR-133 FAQ to get the high-level summary.

Still, looking at the JLS2 and JLS3, we can compare chapter 17 in each, since that's where they seem to define most/all of the rules on interactions between threads. From the JSR-133 FAQ, it seems that the problems they were fixing related to incompletely-constructed objects were in relation to final fields. JLS3 has section 17.5 devoted specifically to this issue. In comparison, JLS2 does not contain the word "final" (or "constructor") anywhere in chapter 17. They give some rules about the ordering or reads and writes for all variables, some other rules about ordering of volatile variables, and some more rules about what happens when locks are involved. But nothing at all is special about final variables or constructors. They are just as vulnerable to out-of-order read/writes as any other variable, it seems. So if a JVM does make an incompletely constructed object visible to another thread, there's nothing in JLS2 we can point to to establish that the JVM is buggy. In JLS3, the problem has been explicitly addressed, and as far as I know, existing implementations of JDK 5 or equivalent do not have this problem.

Note that the discussion above is about final fields. As near as I can tell, nonfinal fields may still be observed to be incompletely initialized under JDK 5. For example:

In theory, I believe it's possible that this code might observe normalField to be uninitialized, but not finalField - under JLS3 rules. However I haven't seen this actually happen. And I might have simply overlooked the relevant rule. But it looks to me like they wanted to beef up the requirements for final fields specifically, while leaving nonfinal fields alone. So you can reliably create immutable objects using final*, and expect them to be thread-safe. But merely omitting mutators from the class is not enough, it seems.

If anyone can find better info about a specific JVM version or benchmark which actually exhibits the problems with final fields under the old model, please let us know.

----

* Obviously making all fields final does not, by itself, make a class immutable. I'm just omitting the additional steps as extraneous to this discussion.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ilja Preuss:

I don't even think it's the compiler which does this, but the processor. Which would, of course, very heavily depend on what the hot spot engine decides to do.


I was under the impression it was the compiler that was allowed to produce bytecode that more or less looked like:

assign something to blah
init blah

Hence something being non-null but not necessarily fully initialized when another thread looks for (something == null) and returns something. But I'm hardly knowledgeable in this and just going on what I've read, which I may have misunderstood of course.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I just realized the JIT is a "compiler" as well so maybe they were talking about that. I do know that there was a similar problem with nested classes that allowed final fields that had already been assigned to be observed uninitialized and it was the bytecode. The new memory model fixed that one too, though I never had the problem personally as it involved calling an overridable method from a constructor which I never do.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think Ilja meant the javac compiler, not JIT. Although javac is allowed to do optimizations, my impression is that often it doesn't bother optimizing that much - instead leaving it to the JIT to decide what optimizations make the most sense under the current runtime conditions.
 
Will Bramble
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ignoring the nitty gritty VM details of how it could come about, if that code snippet of yours Jim could produce inconsistencies then it would seem pretty crazy, wouldn't it?

Just to make sure that I'm not getting this all wrong, are you saying that it's possible that after executing this line:



that the non-final field of the new object f may not be initialised completely from the view of the thread that is subsequently kicked off and tries to access the fields?

Not having a go at you Jim, just saying it really is crazy if this is the case.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's what I'm saying. I don't know if it's correct, but it seems to follow from the statements in the JSR-133 FAQ. I haven't been able to observe it. It is indeed weird, if true. But it also doesn't seem completely unthinkable either. I mean, it's long been known that if you access mutable data without some sort of locking, you can get weird an unpredictable results thanks to the memory model. This is just another example of that - a nonfinal field isn't immutable. Even if there are no mutator methods, the value in this case was changing while in the constructor.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Will Bramble:

Just to make sure that I'm not getting this all wrong, are you saying that it's possible that after executing this line:



that the non-final field of the new object f may not be initialised completely from the view of the thread that is subsequently kicked off and tries to access the fields?

Not having a go at you Jim, just saying it really is crazy if this is the case.


Frankly, I'm not sure whether it's true for this example. From my understanding, it's certainly true if Foo was instantiated after the other thread was started, in Java 1.4 and before.

And it's not at all crazy. Just keep in mind that the whole problem simply vanishes once you synchronize appropriately.
 
Will Bramble
Greenhorn
Posts: 10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
From my understanding, it's certainly true if Foo was instantiated after the other thread was started, in Java 1.4 and before.


Yep, that is more reasonable.

And not questioning the possibility of dirty reads in certain circumstances involving multiple threads. But in the example I think it would be crazy if the sequence of execution does not lead the object to be fully initialised from the view of the thread that is started -afterwards-. If this can't be guaranteed then what else can't we rely on?
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
On further reflection, it is possible that the problem is limited to preexisting threads, not threads started after the object was initialized. I was failing to pay attention to that distinction, and the FAQ which states this sort of behavior has been seen in older JVMs also does not seem to stipulate whether they're talking about preexisting threads or not. So yes, it does seem plausible that threads spawned after the object instantion are safe from observing an incompletely constructed object. Certainly that would account for why I wasn't able to abserve this effect with my test program.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
 
Yuriy Zilbergleyt
Ranch Hand
Posts: 429
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Wow, I somehow kicked off a pretty long discussion
So, if I understand it correctly, in my original example it is possible for the thread running the provided Runnable to try to print out an uninitialized instance variable if that variable had just been set by a different thread? And this would be true even with Java 1.5, as long as the variable isn't declared volatile?

Thank you,
Yuriy
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In your specific example? No, I don't think so.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:
In your specific example? No, I don't think so.


Why not?
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Assuming the Runnable Test is used the way one would normally use a Runnable, it would be instantiated, then passed to a Thread instance, and then the thread is started. So run() would access variable s from a thread started after the constructor completed, which is the situation that we just discussed - we think that's safe. However the original code didn't actually show how the Test class was really being used, so I suppose we don't really know. There are various strange ways the class could be used which would allow a preexisting thread to access the nonfinal variable s, in which case all bets are off.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Jim Yingst:
Assuming the Runnable Test is used the way one would normally use a Runnable, it would be instantiated, then passed to a Thread instance, and then the thread is started. So run() would access variable s from a thread started after the constructor completed, which is the situation that we just discussed - we think that's safe.


Ah, but as we have a setter for s, the *object* s references could well have been instanciated *after* the thread has been started. Therefore I think it is *not* safe.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, wasn't the question here specifically about incomplete construction? The unsynchronized set method can certainly cause other problems after construction has completed. I was just looking at the question of whether, immediately after creating a Test instance, it is possible that s is still null? And we think the answer to that is no, correct?
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Jim Yingst:
Well, wasn't the question here specifically about incomplete construction?


Ah, yes - s certainly has been fully initialized once.

But the String referenced by s can look like incompletely constructed from the Thread that runs the run method.
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Prior to JDK 5, yes. But the fields of a String are all final, right? So the memory model fix should ensure that an incompletely constructed String can't be seen fromm another thread. As I understand it, anyway.
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ilja Preuss:
Why not?


Because nowhere in their example do they have code that would be able to observe a partially initialized object. It might be possible to create an example that passes the method a partially initialized object, but their example doesn't do that.
 
Yuriy Zilbergleyt
Ranch Hand
Posts: 429
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ken Blair:


Because nowhere in their example do they have code that would be able to observe a partially initialized object. It might be possible to create an example that passes the method a partially initialized object, but their example doesn't do that.


I guess maybe I wasn't very clear in my question. Assume the following code is run:

 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Jim Yingst:
Prior to JDK 5, yes. But the fields of a String are all final, right?


Not in JDK 1.4. Don't know about 5...
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry, I misspoke - I was just thinking about JDK 5 here. All fields of String are indeed final in JDK 5, except for the hash code, which is lazily evaluated. There's also no longer any sharing of the backing char[] array between a String and its substrings, or between String and StringBuffer. They've simplified the code in this area somewhat.

So anyway, under JDK 5 is should be impossible to see an incompletely constructed String from another thread. Prior to JDK 5, it was apparently possible.
[ January 27, 2006: Message edited by: Jim Yingst ]
 
Ken Blair
Ranch Hand
Posts: 1078
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Yuriy Zilbergleyt:


No. It would be impossible for the parameter to be partially initialized there. You'd have to have one thread creating the String and assigning to a member variable and then have another thread accessing that member variable and then if the compiler reorders the writes then maybe it can get that member variable while it's only partially initialized. If it then passed that to setS() before the other thread finished initializing it and then if the third thread doing the printing actually got it through to the output stream before the first thread finished initialization maybe it could happen.

At least that's how I understand it.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!