Valhalla user-model

João Mendonça jf.mend at gmail.com
Wed Jun 15 09:34:47 UTC 2022


Hello,


I would like to bring to your consideration the following set of
observations and user-model suggestions, in the hope that they will bring
some useful ideas to the development of the Valhalla project.



*Definition*
*shared-mutable* - a variable that is mutable (non-final) and can be shared
between threads; shared-mutables are the non-final subset of the
shared-variables (§17.4.1.
<https://docs.oracle.com/javase/specs/jls/se18/html/jls-17.html#jls-17.4.1>)



*Observations*
Shared-mutables are the only variables that have these two apparently
independent properties:

   1. lack definite-assignment (§16.
   <https://docs.oracle.com/javase/specs/jls/se18/html/jls-16.html>) - the
   variable is initialized with a default-value if not definitely-assigned (
   §4.12.5.
   <https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.12.5>
   )
   2. allow data-races - the variable may be read/written while being
   written by another thread, with both events happening in an unpredictable
   order (§17.4.5.
   <https://docs.oracle.com/javase/specs/jls/se18/html/jls-17.html#jls-17.4.5>
   )

Via properties 1 and 2, nullability and encoding-mode, respectively, affect the
semantics of variables in a way that is unique to shared-mutables:

   1. if not definitely-assigned, the variable is initialized with:
      - if nullable: the null value, regardless of type
      - if not nullable: the zero-value of the type
   2. in a data-race, the value read/written:
      - if reference: has unpredictable origin in *one* of the various
      writes
      - if inline, either:
         - has unpredictable origin in one of the various writes
         - is torn i.e. has distinct internal parts with separate
         unpredictable origins in *more than one* of the various writes (
         §17.7.
         <https://docs.oracle.com/javase/specs/jls/se18/html/jls-17.html#jls-17.7>
         )


These are the 3 kinds of shared-mutable variables (§4.12.3.
<https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.12.3>):

   - non-final class variables
   - non-final instance variables
   - array components

The remaining kinds of variables don't have any of the above properties:

   - final class variables
   - final instance variables
   - method parameters
   - constructor parameters
   - lambda parameters
   - exception parameters
   - local variables



*Ideal user-model*

In this user-model, the encoding-mode is not complected with nullability.

For class-authors:

   - *value-knob* to reject identity - Applicable on class declarations, if
   used by a class-author to indicate that the class instances don't require
   identity (a value-class), the runtime will be free to copy these values and
   choose between reference or inline encoding everywhere except in
   shared-mutables, as doing so does not introduce any semantic changes to the
   program. In shared-mutables, however, value-instances can only be inlined
   if atomicity is guaranteed, which will depend on the hardware and the
   variable bit-size (value plus nullability).
   - *tearable-knob* to allow tearing - Applicable on value-class
   declarations, may be used by the class-author to hand the class-user the
   responsibility of how to avoid tearing, freeing the runtime to always
   inline instances in shared-mutables (bikeshedding: when dealing with
   tearable value-classes, the "terrible" sound can work as a warning for
   the dangers of neglecting this responsibility). Conversely, if this knob
   is not used, instances will be kept atomic, which allows the class-author
   to guarantee constructor invariants, which may be useful for the class
   implementation and class-users to rely upon.
   - *no-default-knob* to forbid using the zero-value as a default -
   Applicable on value-class declarations, may be used by the class-author to
   force shared-mutables of this type to either be definitely-assigned or
   nullable. (Actually, I don't believe this knob will be useful, since I
   think all zero-values are equally bad: moving across the spectrum from
   useful to useless to out-of-domain default-values is moving from hidden to
   noticeable to obvious missed-initialization-bugs. Ex:  false -> 0.0 -> Jan
   1, 1970 -> 0/0 -> null. The only good solution is definite-assignment on as
   many kinds of shared-mutables as possible.)

For class-users:

   - *nullable-knob* to include null in the value-set of variables -
   Applicable on any variable declaration. In either encoding-mode, the
   runtime is free to choose the encoding for the extra bit of information
   required to represent null. In shared-mutables that are not
   definitely-assigned, controls the default-value: either null or the
   zero-value of the type. Since identity-types lack a zero-value, any
   non-nullable shared-mutable with an identity-type must be
   definitely-assigned. If, on some kinds of shared-mutables,
   definite-assignment can't be enforced, then those kinds of variables
   (probably array components) cannot have an identity-type and be
   non-nullable.
   - *atomic-knob* to avoid tearing - Applicable on shared-mutable
   declarations, may be used by the class-user to reverse the effect of the
   tearable-knob, thereby restoring atomicity.




*Complected user-model*
If, for compatibility, the encoding-mode must remain complected with
nullability, the above user-model can be adapted as follows.

The knobs for class-users are replaced with:

   - *inline-knob* to require inline encoding - Applicable on
   shared-mutable declarations with a value-type, gives the class-user
   control over the tradeoffs between performance, footprint, nullability,
   nullability's implied default-values, and how to avoid tearing.

The knobs for class-authors get their scope restricted:

   - *value-knob* - by itself, no longer allows the runtime to inline any
   value-instances on shared-mutables.
   - *tearable-knob* - only applicable if the instance bit-size is higher
   than 32 bits, will simply enable/prohibit using the inline-knob on
   variables of this type.
   - *no-default-knob* - only affecting inline shared-mutables of this
   type, forces them to be definitely-assigned. For any kind of
   shared-mutable where definite-assignment can't be enforced, this knob
   prevents those variables (probably array components) to have values of this
   type encoded inline. If, on the other hand, definite-assignment can be
   enforced in all kinds of shared-mutables, then it should always be
   enforced, as this solves both the useless zero-values problem and the
   missed-initialization-bugs problem. However, in that case, this knob no
   longer has a reason to exist.


Kind regards,
João Mendonça
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20220615/7afbd86b/attachment-0001.htm>


More information about the valhalla-spec-observers mailing list