[9] RFR(S): 8005873: JRuby test_respond_to.rb asserts with: MT-unsafe modification of inline cache

Remi Forax forax at univ-mlv.fr
Mon May 19 21:42:54 UTC 2014


Hi John,
i don't understand how the Jit can remove the memory barrier due to the 
synchronized when prev is not null ?

Rémi

Envoyé avec AquaMail pour Android
http://www.aqua-mail.com


Le 19 mai 2014 21:52:20 John Rose <john.r.rose at oracle.com> a écrit :

> On May 15, 2014, at 2:04 AM, Vladimir Ivanov <vladimir.x.ivanov at oracle.com> 
> wrote:
>
> > Cache is written only once, so it has only 2 states (null and non-null 
> value) during it's lifecycle.
> > The only stale value cachedLambdaForm() can see is null, but then the 
> caller tries to initialize the cache and after acquiring the lock in 
> setCachedLambdaForm() sees actual value.
> > So, the worst thing can happen if readers aren't synchronized is creation 
> of unnecessary LF (which go dead right away) in rare cases.
>
>
> Lazy evaluation is a pattern that we use (or try to use) in a lot of 
> places.  It's important to understand it and get it right.
>
> With JDK-8005873, the root cause is that lazy evaluation is producing 
> multiple right answers, and an assert is throwing a panic.  The correct fix 
> is to use a CAS or lock (or equivalent) to make sure that, even if several 
> threads race to produce a right answer, only one right answer gets 
> published and used.
>
> I coded this completely free of locks, in 'setCachedLambdaForm', because I 
> thought that multiple right answers would be harmless.  That's arguably 
> still correct, but if it makes assertions fire, we should clean up the edge 
> case, rather than detune the assert.
>
> Uniquification can be done with a lock or a CAS.  CAS is preferable, but 
> hard to code.  AtomicReferenceArray provides easy access to CAS, but you 
> pay a cost for simple 'get', since that is volatile.  That's not desirable; 
> in fact the @Stable property is more appropriate.  On balance I would 
> prefer a simple lock, such as this:
>
> +    synchronized
>      public LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
> -        // Should we perform some sort of CAS, to avoid racy duplication?
> +        // Simulate a CAS, to avoid racy duplication of results.
> +        LambdaForm prev = lambdaForms[which];
> +        if (prev != null)  return prev;
>          return lambdaForms[which] = form;
>      }
>
> Once lazy results are guaranteed unique the rest of the algorithm is fine, 
> I think.  See below.
>
> — John
>
> P.S.  More discussion of lazy evaluation and races, assuming published 
> values are unique.
>
> If the state variable behind a lazy evaluation is a pointer to 
> safely-published data, then a racy read will pick up either the initial 
> state (null) or the desired data.
>
> A slow path for null detection needs to reliably pick up the true value of 
> the variable, to avoid starvation and multiple initialization.  Doing the 
> slow-path read and write inside of a lock satisfies the requirements of the 
> JMM for correct race-free code.
>
> Outside of the lock, it is important that anything observed by the racy 
> read eventually lead to a correct answer, even if the racy read gives an 
> out-of-date answer.
>
> If the lock's critical section is trivial (no object construction or side 
> effects) then a CAS is usually faster, but it is hard to code correctly.  
> The new JMM will fix this we hope and make it possible to perform safe and 
> sane CAS operations.  Meanwhile, the atomic reference array would work, but 
> it is (IMO) overly expensive since it performs
>
> The bottom line is that we don't need any volatile references on the fast path.
>
> One hazard is that the JMM does not guarantee safe publication in either of 
> the following two cases:
>
> Caveat #1. Data is accessed along a path that does not use a final field.
> Caveat #2. Data is accessed along a path that has been modified since 
> construction.
>
> Caveat #1 is usually (by itself) not a problem, if the access path goes 
> through an object with at least one final field.  (That's because existing 
> implementations use a broad fence at the end of a constructor if an object 
> contains any non-static finals.)  And the new JMM may further downgrade or 
> remove Caveat #1.
>
> Caveat #2 includes the case of a non-final field in a "mostly final" 
> object.  The field LambdaForm.vmentry is such a field.  Any non-final 
> fields accessed through a vmentry patched into a published LambdaForm are 
> racy, unless some sort of fence has been issued before the vmentry object 
> is patched into the lambda form.
>
> Caveat #2 It also includes the case where the path goes through an array 
> element which was changed since the root object was constructed.  This 
> happens in various tabular structures where an array that starts out full 
> of nulls ends up containing lazily evaluated entries.  The entries should 
> each be safely published.
>
> Caveat #2 does *not* apply to stable array substructures like 
> LamdaForm.names, if (as is the case with 'names') the arrays are assigned 
> before the enclosing object is fully constructed, and the array references 
> themselves are final.
>
>


More information about the hotspot-dev mailing list