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