Hi John,
for me it's not clear if the sentinel has to be user controlled or not (think 
vulls by example) for getfield, and given it looks like solving how to do a CAS 
on a value type (and its interaction with vulls), something we are still 
working on, i think we should restrain ourselves to try to solve getfield on a 
lazy final before our work on value type is finished.

As you said, for getstatic, we don't have this issue that why i think we should 
design getstatic of a lazy final without disabling null, getfield will likely 
have more constraints and that's fine.

Rémi

----- Mail original -----
> De: "John Rose" <john.r.r...@oracle.com>
> À: "Remi Forax" <fo...@univ-mlv.fr>, "Maurizio Cimadamore" 
> <maurizio.cimadam...@oracle.com>, "Brian Goetz"
> <brian.go...@oracle.com>
> Cc: "valhalla-spec-experts" <valhalla-spec-experts@openjdk.java.net>
> Envoyé: Dimanche 3 Mars 2019 00:15:16
> Objet: Re: lazy statics design notes

> Remi, Maurizio, Brian, I shot my last round, and I'm out.
> I agree we shouldn't tinker with the (value sets of the) types.
> 
> Instead let's reach for ways to adjoin extra sentinel values
> in the case of lazies (and optionals, and lazies of optionals),
> of both null-default and zero-default types.  These sentinel
> values will encode as disjoint from the base value set of the
> type T (whether T is null-default/ref or zero-default/prim).
> 
> Sentinels will denote the states outside of the normal "T value
> is present" state, either:  unbound-lazy or empty-optional.
> A lazy optional needs both sentinels, while a plain lazy or
> optional needs just one.
> 
> In the case where T is a reference, the JVM might add in one
> or two new references (perhaps with tag bits for extra dynamic
> checking).  This can be done outside the safe type system in
> the case of the JVM, if it puts the right decoding barriers in
> the right places, to strip the sentinels before using them in
> a T-safe manner.
> 
> In the case where T's encoding space is fully tensioned (like int)
> the sentinel will have to take the form of an extra field of
> two states.  One is "I'm the sentinel" and the other is "there's
> a T value in my other component".  This is just Optional all
> over again, which uses a sentinel (null!) today.
> 
> (If two sentinels are required, for a lazy-optional, then the extra
> field can take three states.  Or we append two extra fields.)
> 
> If we are buffering T on the heap in a stand-alone object, the
> extra state can (with some ad hoc hacking) be folded into the
> object header, because it is almost certain that the object header
> has some slack that's usable for the purpose.  Since buffered value
> object's won't need to store synchronization state (individually,
> at least), the bits which usually denote synchronization state can
> be co-opted to store a sentinel state, for a buffered T.  This usually
> won't be necessary, though, since if a T value is buffered, the
> client that is holding the reference is also capable of holding
> a real null, which more directly represents an out-of-type value
> point for T.  This is today's situation with Integer, which is
> null-default, while its payload type int is zeroable but not nullable.
> 
> If we were to load a value-like Integer onto the stack, the extra
> sentinel field would have to be manufactured like this:
>    boolean hasPayload = (p == null ? false : true);
>    int payload = (p == null ? int.default : p.value);
> This pair of values on stack would act like a value type whose
> default zero bits encode null, while an ordinary int payload value
> would be accompanied by a 'true' bit that distinguishes it from
> the null encoding.  This value type should, of course, be null-default,
> even though it carries a zero-default payload.
> 
> In the case where T's encoding space has some slack (like boolean)
> a sentinel or two can be created by using unencoded bit patterns.
> If T is a value type containing a reference or floating point field,
> then the option exists to "steal" the encoding from inside that field.
> 
> The all-zero-bits state is favorable in the heap because it is most
> reliably the first state of the object.  In the case of both optional
> and lazy (lazy-optional is just lazy here), the sentinel encodes
> the initial state, which encourages us to implement the sentinel
> with a default value (zero or null) for T.  This means that the normal
> corresponding default (zero or null) should actually be encoded
> with a special sentinel value.
> 
> On the stack the all-zero-bits state is less directly useful, but of
> course it's good if the stack and heap encodings can be as close
> as possible.
> 
> The getfield operation which loads a lazy instance field should do
> two things: 1. check for the encoding of the unbound state (which
> should be all-zeroes), 2. check for the encoding of the bound-to-default
> state (which should be a specially crafted sentinel).  In case 1, the
> lazy value binding code must be executed.  In case 2, the sentinel
> must be replaced by a true default value.  Something like this probably
> needs to happen anyway for null-default value types, since the
> zero-default encoding of a null-default value type needs to be
> replaced by a null pointer when it is loaded.
> 
> It looks to me like there are at least three places where a "raw"
> value is "wrapped" to give it adjusted semantics.  First, a null-default
> value type wraps the underlying zero-default bits by swapping
> out the zero and swapping in the null.  Second, an optional wraps
> the internal value by adjoining the "empty" value point.  Third,
> a lazy wraps its non-lazy value by adjoining the "unbound" state.
> 
> Sentinels are just one way to do it; surely there are others.  But if
> you don't use sentinels in some capacity to overlay new values on
> T's value set, you probably need a side bit to convey the variable's
> state; as I've said before, managing that correctly seems to require
> transactional memory.
> 
> Condy doesn't require a sentinel.  But of course HotSpot *internally*
> uses a sentinel to distinguish a resolved null value from the unresolved
> state.  The unresolved state is a null pointer while a resolved null
> is a special out-of-type non-null reference (called "the null sentinel")
> which condy swaps out for a resolved null after it does the null check.
> That's the same trick as I've described above.  Surprise; I wrote it.
> Great minds may think alike, but mediocre minds think the same
> thing repeatedly.
> 
> — John

Reply via email to