[concurrency-interest] synchronized constructors

Roland Kuhn rk at rkuhn.info
Sun Dec 18 16:02:18 EST 2011


Hi Vitaly,

On Dec 18, 2011, at 21:42 , Vitaly Davidovich wrote:

> I think David's point is that you're relying on implementation details rather than JMM to ensure correctness.  According to JMM, your code does not create any happens-before edge because your getX() does not synchronize on the same lock, so there's no happens-before edge between the write and the read.  In practice though, it'll work since the compiler does not track edges to this extent.
> 
I don’t know from where in the JMM this comes (if at all), but I am relying on the JVM spec, which forbids a prescient write to cross a “lock” operation.

> In your case, I still think it's better to either use an AtomicReference with lazySet/get to ensure initializing writes are visible or you can use Unsafe.putOrderedObject if you can't use AtomicReference.  On TSO archs, this is the cheapest fence since it's actually a no-op at the assembly level, and only a compiler barrier.
> 
The question was about whether creation of extra objects (which is a rather high cost) can be avoided. And I am not sure that a lazySet would achieve what I describe above.

> I'm not sure if you actually have code that needs to work like this, but I would strongly recommend that you use "normal" safe publication approaches and not engage in these gymnastics.  If you have 3rd party code that requires jumping through these hoops, you should ask them to correct their implementation.
> 
Well, the problem is a different one: imagine you are a library writer, actually a pretty widely used library, and you have to introduce a non-final field in a class which did not ever have one before. This changes the class from “totally safe” to “must use safe publication”, which is a silent change of semantics. Now, how do I make that object with the non-final field as safe as the previous version, which had only final fields?

So, I still think that my code below does the trick, unless someone points me to the glaring hole in it.

> As a personal aside, I feel that something like the new C++11 memory model is the right way to go forward in allowing developers finer grained control over fences.  Instead of having special provisions for final and volatile members, it makes you specify the consistency/order at the callsite rather than at variable declaration.  If you think about it, a field being "volatile" doesn't really make sense -- it's not the field that's volatile, it's certain reads/writes from/to it that need certain order guarantees, and not all callsites require the same level of these.  Joe Duffy had a similar blog on this for C#:  http://www.bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx 
> 
Yes, I would very much welcome a simple way achieving a fence, as you might imagine ;-)

Regards,

Roland

> Vitaly
> 
> On Sun, Dec 18, 2011 at 10:09 AM, Roland Kuhn <rk at rkuhn.info> wrote:
> Yes, inspired by the JSR-133 cookbook I was alluding to roach motel semantics: the synchronized is just there to ensure that the volatile write has happened before the possibly non-volatile write of the newly constructed object’s reference to a possibly shared location is actually performed. This would mean that nobody can obtain a reference before initialization is done. And then I would like to arrive at the conclusion that the volatile read of “x” from a different thread must synchronize-with the initial write.
> 
> Does this work?
> 
> Roland
> 
> On Dec 18, 2011, at 00:40 , David Holmes wrote:
> 
> > Roland,
> >
> > An empty synchronized block in a constructor, when there are no other
> > synchronized regions of code, gains you nothing under the JMM as there are
> > no happens-before edges established. Are you
> > relying on the implementation of synchronized to issue specific memory
> > barriers here?
> >
> > David
> >
> >> -----Original Message-----
> >> From: Roland Kuhn [mailto:rk at rkuhn.info]
> >> Sent: Sunday, 18 December 2011 1:46 AM
> >> To: dholmes at ieee.org
> >> Cc: Zhong Yu; concurrency-interest at cs.oswego.edu
> >> Subject: Re: [concurrency-interest] synchronized constructors
> >>
> >>
> >> Thank you, David and Hans, for clarifying these semantics. The
> >> OP’s question has been answered, and I think I can also derive an
> >> answer for my question. I’ll try to wrap it up below, please
> >> correct or confirm:
> >>
> >> class IAmSafe {
> >>  private volatile int x;
> >>  IAmSafe() {
> >>    x = 5;
> >>    synchronized(this) {}
> >>  }
> >>  public int getX() {
> >>    return x;
> >>  }
> >> }
> >>
> >> Assuming that the java.lang.Object constructor is well-behaved,
> >> it should be impossible under any circumstances (barring
> >> reflective modification) that a call to getX() returns anything
> >> but 5, right?
> >>
> >> If this is true, then the cheapest solution to my multi-stage
> >> construction problem—where I completely control the program flow
> >> up to the point where I consider the object initialization
> >> finished—on x86 processors looks like this:
> >>
> >> class ComplexDeps {
> >>  final int someValue;
> >>  ComplexDeps(int someValue) {
> >>    this.someValue = someValue;
> >>  }
> >>  // stage 2
> >>  private volatile OtherThing thing;
> >>  public void init(OtherThing thing) {
> >>    this.thing = thing;
> >>    synchronized {}
> >>  }
> >>  // other stuff which reads someValue & “thing”, never writes to
> >> “thing” and runs only after init() in program order
> >> }
> >>
> >> IIUC, there will be a (half-way) expensive fence-like instruction
> >> emitted at the end of init(), but apart from that all read
> >> accesses after construction should be cheap. Any nobody will ever
> >> see “thing” uninitialized.
> >>
> >> Regards,
> >>
> >> Roland
> >>
> >> On Dec 17, 2011, at 09:57 , David Holmes wrote:
> >>
> >>> Correction ...
> >>> I wrote:
> >>>> Zhong Yu writes:
> >>>>> Suppose constructor java.util.Vector() is synchronized
> >>>>>
> >>>>>   static Vector v;
> >>>>>
> >>>>>   // thread 1
> >>>>>   v = new Vector();
> >>>>>
> >>>>>   // thread 2
> >>>>>   Vector r = v;
> >>>>>   if(r!=null)
> >>>>>     r.add(x);
> >>>>>
> >>>>> The last line can throw exception, because thread 2 can observe the
> >>>>> blank state of the object
> >>>>
> >>>> No it can not.
> >>>> Completion of the constructor happens-before the reference is
> >> published.
> >>>
> >>> Delete this sentence. It's wrong but not relevant to the argument.
> >>>
> >>> David
> >>> -----
> >>>
> >>>
> >>>> The acquiring of the monitor in add() can only happen when the
> >>>> monitor is released by the constructor. The release of the
> >>>> monitor in thread
> >>>> 1 happens-before the acquire of the monitor by thread 2. All the
> >>>> constructor
> >>>> actions happen-before the release of the monitor.
> >>>>
> >>>> The publishing of the Vector can not be moved prior to the
> >> acquisition of
> >>>> the monitor in the constructor ("roach motel semantics").
> >>>>
> >>>> David
> >>>> -----
> >>>>
> >>>>
> >>>>> , yet Vector.add() presumes a> post-construction state.
> >>>>
> >>>>> Zhong Yu
> >>>>>
> >>>>> On Fri, Dec 16, 2011 at 7:52 PM, David Holmes
> >>>>> <davidcholmes at aapt.net.au> wrote:
> >>>>>> Zhong Yu writes:
> >>>>>>> If the reference is unsafely published, another thread can get the
> >>>>>>> reference early; it then calls an instance method which may
> >>>> obtain the
> >>>>>>> lock before the creation thread can obtain the lock for the
> >>>>>>> constructor. Therefore the other thread can observe the blank state.
> >>>>>>> As Ruslan corrected me, no partial state can be observed though.
> >>>>>>
> >>>>>> The only way this can happen, if you synchronize the whole
> >>>>> constructor body,
> >>>>>> is if a super class constructor does the unsafe publishing. But
> >>>>> in that case
> >>>>>> there is no such thing as safe-publishing because the object
> >>>> can escape
> >>>>>> before the subclass constructor does any initialization.
> >>>>>>
> >>>>>> To summarize a long and garbled thread. If the constructor body is
> >>>>>> synchronized, there is no accessible state and all methods are
> >>>>> synchronized,
> >>>>>> then no external user of the class can publish a reference in a
> >>>>> way that is
> >>>>>> unsafe. If the constructor does the publishing within the
> >> synchronized
> >>>>>> block, it is still safe. Only if the superclass does it can it
> >>>>> possibly be
> >>>>>> unsafe.
> >>>>>>
> >>>>>> Also to address an other point: lock elision is allowed (eg
> >>>> using escape
> >>>>>> analysis) but the memory synchronization effects must remain
> >>>>> (you can lose
> >>>>>> enforced mutual exclusion [as you don't need it], but not
> >>>> happens-before
> >>>>>> edges).
> >>>>>>
> >>>>>> Constructors can't be synchronized simply because back in 1995 no one
> >>>>>> realized there could be a need for it. It can be mostly
> >>>> worked around by
> >>>>>> using a synchronized block. But you can't synchronize the
> >>>>> invocation of the
> >>>>>> super constructor.
> >>>>>>
> >>>>>> End of story :)
> >>>>>>
> >>>>>> Cheers,
> >>>>>> David
> >>>>>>
> >>>>>
> >>>>
> >>>> _______________________________________________
> >>>> Concurrency-interest mailing list
> >>>> Concurrency-interest at cs.oswego.edu
> >>>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>>>
> >>>
> >>> _______________________________________________
> >>> Concurrency-interest mailing list
> >>> Concurrency-interest at cs.oswego.edu
> >>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >>
> >> --
> >> Simplicity and elegance are unpopular because they require hard
> >> work and discipline to achieve and education to be appreciated.
> >>  -- Dijkstra
> >>
> >>
> >
> 
> --
> I'm a physicist: I have a basic working knowledge of the universe and everything it contains!
>    - Sheldon Cooper (The Big Bang Theory)
> 
> 
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> 
> 
> 
> -- 
> Vitaly
> 617-548-7007 (mobile)

--
Simplicity and elegance are unpopular because they require hard work and discipline to achieve and education to be appreciated.
  -- Dijkstra

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20111218/67f87fe0/attachment-0001.html>


More information about the Concurrency-interest mailing list