SourceForge.net Logo
Main Overview Wiki Issues Forum Build Fisheye
Issue Details (XML | Word | Printable)

Key: CMP-269
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Minor Minor
Assignee: Shay Banon
Reporter: David Smiley
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Compass

double-check locking idiom in CompassSettings.getSettingGroups

Created: 21/Sep/06 05:25 PM   Updated: 02/Feb/07 11:53 AM
Component/s: Compass::Core
Affects Version/s: 1.1 M2
Fix Version/s: 1.1 M3


 Description  « Hide
The double-check locking idiom doesn't work. I found an occurrence of this anti-pattern here: CompassSettings.getSettingGroups(). All that needs to be done is to move up the "synchronized(groups){" line up four lines so that the "groups" Map is not looked at outside of synchronization.

 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Shay Banon added a comment - 22/Sep/06 06:30 AM
Don't tell me that you run into a problem with this one ? CompassSettings is used once when Compass is configured, which is done within a single thread context.

David Smiley added a comment - 22/Sep/06 07:41 AM
No, I didn't, and if I did I probably wouldn't have known it was the problem.

If you are correct in that CompassSettings is only used once (by a single thread) and never used again, then you can dispense with synchronization altogether. You ought to null out the reference to ensure it actually can't be used again. Leaving the code the way it is is misleading.


Shay Banon added a comment - 22/Sep/06 07:57 AM
By the way, the worst thing that can happen is that the processing of the settings group would happen twice. The gain is that there is no synchronization block when checking if it is cached. Does it make sense?

I could make it more bullet proof by adding the same check done before the sync block within the sync block as well.


David Smiley added a comment - 22/Sep/06 08:39 AM
1. As I said, if only one thread is using this, dispense with synchronization and null the reference to prevent accidental use of CompassSettings afterwards.

If multiple threads might access getSettingGroups simultaneously ...

2. Is this code so performance sensitive that you feel there is a need to attempt to not synchronize? Did you actually take measurements to know it is so? I suspect not.

3. You are wrong about the worse case. Say one thread, the first one, makes it into the sychronized block and is part way into creating the inner "groupSettings" Map. The VM/processor/compiler might decide to actually store the reference to "gropuSettings" inside the "groups" map before "groupSettings" is fully populated. Meanwhile, another thread finds this Map in "groups" outside the synchronized block and never needs to enter the synchronized block because it found the corresponding "groupSettings". However, the "groupSettings" isn't finished being initialized. See, the VM/processor/compiler is allowed to do that re-ordering because the the other thread never synchronized on "groups". Shay, even if you don't understand/agree with what I'm saying, please just do #1 above or move the synchronized code around the use of "groups", depending on wether multiple threads will actually hit this code. Thread-safety is a poorly understood problem. It's a pet interest of mine.


Shay Banon added a comment - 24/Sep/06 11:47 AM
First, lets set some ground rules. I am more than happy to discuss Compass possible bugs or enhancements, but something like: Even if you don't understand/agree .. just do it .. does not work here.

As for this issue, it is important for me to implement this correctly. I agree that in terms of performance it won't be that bad. There is a case that I just thought about, where people can plug in custom code into Compass, where they might use this code in a multi threaded scenario. Another reason for wanting to implement this correctly is because this idiom is used in another place in Compass, which is used in a multi threaded env and important to be as concurrent as possible,

As for number 3, I think that what you are saying is that the groups.put(settingPrefix, map) might happen before the map variable is fully initialized (has nothing to do with the groupSettings, since it is not the one you check on before the sync block). I do not believe that the compiler/vm can perform this optimization, because of how to code is structured. Can you double check it again?

Last, Compass now comes with backport util concurrent, so I can use a ConcurrentHashMap for groups and completely remove the synchronized block. What do you think?


David Smiley added a comment - 24/Sep/06 11:22 PM
Sorry about my insistance of how you handle this. I've tried to explain to other programmers that there are threading issues in their code but for some reason, people tend to be stubborn or indifferent on this subject.

ConcurrentHashMap looks like the right choice. There is a lingering concern I have with that, though. I know it works fine in Java 1.5, though I am concerned if it actually works for 1.4. I thought the Java memory model was cleaned up in 1.5... so all the "volatile" assumptions that ConcurrentHashMap makes aren't correct for 1.4 – I think. I'll have to do more digging and maybe ask on the JMM mailing list.


Shay Banon added a comment - 26/Sep/06 02:11 AM
I have changed it to ConcurrentHashMap. If all is well, I will close this issue.

David Smiley added a comment - 26/Sep/06 07:41 AM
I asked on the Java Memory Model mailing list about volatile semantics in Java 1.4. Jeremy Manson informed me that the "dirty little secret" is that volatile works in "most" 1.4 VMs, and that any deviation is looked at as an error. He didn't know which one(s) were problematic. So... I think it's reasonable to go with ConcurrentHashMap.

Shay Banon added a comment - 26/Sep/06 09:22 AM
Thanks for the effort mate, I really appreciate it!. I guess it means that I can close this issue. If nothing pops up I will close it tomorrow.