This is a tracking bug to follow up on the questions in these Emails:
http://mail.openjdk.java.net/pipermail/valhalla-dev/2020-January/006706.html
http://mail.openjdk.java.net/pipermail/valhalla-dev/2020-January/006710.html
Every path that first publishes a buffered inline instance, from the thread
that created it, *must* issue a store-store barrier before publication and
after the inline value fields have been buffered. It seems to be the case
that some such paths in the Valhalla VM are *not* protected, and this
may cause race conditions which are not allowed by the JVMS.
(Instead of a store-store barrier an equivalent safe publication technique
could be used, such as a safepoint, as used by the GC to publish the
bulk results of a copy phase.)
Detail:
Value types are allowed to tear in user-visible heap variables
(unless marked “always atomic” via an upcoming feature).
But buffers introduced on the fly by the JVM are not user-visible
variables. They are not even variables; they are immutable;
this strengthens my point, though doesn’t make it completely.
JVM-introduced buffers must be protected from tearing, by
ensuring that before each such buffer is published all stores
to its value's fields are fully settled. This requires a store-store
barrier before the publishing store.
The JVM has freedom to unbuffer and rebuffer values
between any two instructions (remember deopt & reopt),
so the tearing of a heap buffer would appear as tearing of a
value on stack or in a local. But the JVMS does not allow
race conditions to affect stacked or local values.
The JVMS does allow race conditions on multi-field values
stored in heap variables. But no race conditions should
be allowed if a heap variable is implemented as an invisible
reference to a buffered value. In that case, the buffered
value must always be fully settled for safe publication
before the invisible reference is stored to the heap variable.
This extra condition is perhaps not strictly necessary, but it is
probably *practically* necessary, because any race conditions
that might appear on such a buffered value must be attributable
to a particular variable (of value type) in the heap, and the only
variable visible to the user is the invisible reference, which is
inherently non-tearing. Any races on the indirect buffer would
have to be attributable to the reference field and controllable
by locking its containing object. But if the invisible reference
escapes (via copying) to another place, such as the stack
or a local or another invisible reference variable in the heap,
then any pending races on the buffer will suddenly appear
in the wrong place. The race gets misplaced because a
new alias (reference) appears to the buffer. The best way
to rule this out is to safely publish the buffer in the first place,
rather than try to prevent misplaced races after an unsafe
publication.
http://mail.openjdk.java.net/pipermail/valhalla-dev/2020-January/006706.html
http://mail.openjdk.java.net/pipermail/valhalla-dev/2020-January/006710.html
Every path that first publishes a buffered inline instance, from the thread
that created it, *must* issue a store-store barrier before publication and
after the inline value fields have been buffered. It seems to be the case
that some such paths in the Valhalla VM are *not* protected, and this
may cause race conditions which are not allowed by the JVMS.
(Instead of a store-store barrier an equivalent safe publication technique
could be used, such as a safepoint, as used by the GC to publish the
bulk results of a copy phase.)
Detail:
Value types are allowed to tear in user-visible heap variables
(unless marked “always atomic” via an upcoming feature).
But buffers introduced on the fly by the JVM are not user-visible
variables. They are not even variables; they are immutable;
this strengthens my point, though doesn’t make it completely.
JVM-introduced buffers must be protected from tearing, by
ensuring that before each such buffer is published all stores
to its value's fields are fully settled. This requires a store-store
barrier before the publishing store.
The JVM has freedom to unbuffer and rebuffer values
between any two instructions (remember deopt & reopt),
so the tearing of a heap buffer would appear as tearing of a
value on stack or in a local. But the JVMS does not allow
race conditions to affect stacked or local values.
The JVMS does allow race conditions on multi-field values
stored in heap variables. But no race conditions should
be allowed if a heap variable is implemented as an invisible
reference to a buffered value. In that case, the buffered
value must always be fully settled for safe publication
before the invisible reference is stored to the heap variable.
This extra condition is perhaps not strictly necessary, but it is
probably *practically* necessary, because any race conditions
that might appear on such a buffered value must be attributable
to a particular variable (of value type) in the heap, and the only
variable visible to the user is the invisible reference, which is
inherently non-tearing. Any races on the indirect buffer would
have to be attributable to the reference field and controllable
by locking its containing object. But if the invisible reference
escapes (via copying) to another place, such as the stack
or a local or another invisible reference variable in the heap,
then any pending races on the buffer will suddenly appear
in the wrong place. The race gets misplaced because a
new alias (reference) appears to the buffer. The best way
to rule this out is to safely publish the buffer in the first place,
rather than try to prevent misplaced races after an unsafe
publication.
- relates to
-
JDK-8238780 [lworld] C2: Partially initialized value type buffer might be observed by other thread
- Resolved
-
JDK-8236522 NonTearable marker interface for inline classes to enforce atomicity
- Resolved