-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8351569: [lworld] Revisit atomic access modes in flat var handles #1402
base: lworld
Are you sure you want to change the base?
Conversation
👋 Welcome back mcimadamore! A progress list of the required criteria for merging this PR into |
@mcimadamore This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been no new commits pushed to the ➡️ To integrate this PR with the above commit message to the |
ProblemThe VM has several different flat layouts to pick from. Some layouts support atomic load/stores (but require the value payload, including the null channel -- where present -- to fit in 64 bits) -- we call these layouts atomic layouts. Others do not. When value fields/array elements are accessed through the VarHandle API, a question arises: under which conditions can the API support the various access modes provided by the API? Should all access modes be available, regardless of the JVM layout? Or should non-plain access modes only be supported by atomic JVM layouts? The current implementation attempts to do the former -- all access modes are supported all the time. This is done by surrounding atomic operations such as CAS with a SolutionThe VarHandle API should only provide access to non-plain access mode if either a layout is non-flat, or if the layout is flat and atomic. In the former case, we can implement access using One important design consideration is that we want the set of access modes supported by a To provide for more stability I've opted for a simple rule: a field var handle supports atomic operations if either
For arrays, things are more convoluted: as arrays are covariant, it is always possible to create an array for
(Note the above rules mean that var handles targeting migrated value class in JEP 401 will, by definition, support all access modes -- as all uses of such classes will be through nullable types). ChangesIn the code this PR replaces, there are two This first change is to refine the implementation classes so that we can support four cases:
The The decision of whether a given field or array should support non-plain operation is defined in the This PR updates most of the non-plain access primitives for flat values in the Finally, note that even some atomic operations involving reference types have been updated as well -- when a value type is passed to a reference CAS, we need to perform CAS in a way so that it uses the correct definition of TestingThe main new test added here is Some test cases in one JVM test ( |
? new VarHandleReferences.FieldInstanceReadOnly(refc, foffset, type, f.getCheckedFieldType()) | ||
: new VarHandleReferences.FieldInstanceReadWrite(refc, foffset, type, f.getCheckedFieldType())); | ||
} else { | ||
return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this case is not terribly important (typically, if something is non-flat, then it will be "atomic" from a programming model perspective), there are various JVM flags that can affect whether something is flattened or not. It's important here that we don't return a var handles that "overpromises" and support more access modes (just because we know that, underneath, we can use reference Unsafe
primitives) as such behavior might not be stable,
Webrevs
|
return maybeAdapt(vh); | ||
// do not check for atomicity now -- atomicity will be checked on the accessed array object | ||
// a sharper var handle will be obtained (dynamically) from flatArrayElementHandleFor(Object) | ||
return maybeAdapt(new VarHandleReferences.Array(aoffset, ashift, arrayClass)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this works because:
- the reference Array plain access uses array syntax (which works no matter the array passed in)
- non-plain access modes always delegate to sharper var handles based on the type of the accessed array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another note: at the language level there's no mechanism to obtain a class mirror for a null-restricted, or atomic array. This means we can't really create an array var handle that works only on one array shape (which would allow us to eliminate the dynamic dispatch completely, at least for some array var handles).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, now it is crucial for us to benchmark these var handles - I suspect they are very sensitive to profile pollutions.
…lat arrays Cache result of oop-free analysis Beef up tests to check different combinations of VM flags
// try nullable atomic array first | ||
Object expectedArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
Object xArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
if (arrayLayout(expectedArray.getClass()) != layout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part feels a bit dirty: we are receiving a request for layout X - we need to be able to create an array with that same layout -- but there's no primitive for doing that. Which means I have to try using different factories and make sure that I get something compatible (which isn't always guaranteed because of VM flags). All this guesswork should not be necessary -- that is, there should be an unsafe API to create an array with the layout I want (assuming that layout is supported of course)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have considered avoiding the array creation -- but that is messy because it means you have to deal with nulls directly. E.g. if either expected
or x
are null
, what is their corresponding numeric configuration? Going through an array element allows the code to remain agnostic as to how nulls are represented. Another way to avoid array creation would be to have some way to unsafely get the payload of a value as a long, including null channel and everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think runtime support is better for down the road for this part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have decided to address this by adding a primitive to create a flat array with given known layout:
#1404
Once that API is integrated, I will rebase this PR on top of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That API is now in. This patch looks fine to me otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've now simplified the code to use the new primitive.
|
||
// test atomic set with null witness | ||
|
||
assertFalse(vh.compareAndSet(array, 2, null, value1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized that the VH code we have is liberal w.r.t. null
witness values in CAS-like operations -- in the sense that it allows them, even though the underlying variable is null-restricted. For now I decided to preserve this behavior (instead e.g. throwing an IAE) - but better add a test :-)
@@ -39,43 +39,77 @@ VARHANDLES_SRC_DIR := $(MODULE_SRC)/share/classes/java/lang/invoke | |||
# Param 2 - Type with first letter capitalized | |||
define GenerateVarHandle | |||
|
|||
$1_Type := $2 | |||
$1_InputType := $2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to do a bit of trickery here. Basically we have two pair of input values, which should use the same unsafe access primitives:
- Reference, NonAtomicReference -> get/putReference
- FlatValue, NonAtomicFlatValue -> get/putFlatValue
So, we now use InputType
to model the input type to the template, and from there we derive Type
(which is really the Unsafe access type).
There's also some other changes:
- Not all generated classes need non-plain operations (e.g. NonAtomicReference, NonAtomicFlatValue do not)
- Not all generated classes need to handle static fields (e.g. FlatValues and NonAtomicFlatValues do not, as static fields are never flattened)
- Not all generated classes need to deal with arrays (e.g. FlatValues and NonAtomicFlatValues do not -- access to value arrays is always handled in the Reference impl class)
// try nullable atomic array first | ||
Object expectedArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
Object xArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
if (arrayLayout(expectedArray.getClass()) != layout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think runtime support is better for down the road for this part.
Co-authored-by: Chen Liang <[email protected]>
Co-authored-by: Chen Liang <[email protected]>
// try nullable atomic array first | ||
Object expectedArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
Object xArray = ValueClass.newNullableAtomicArray(valueType, 1); | ||
if (arrayLayout(expectedArray.getClass()) != layout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That API is now in. This patch looks fine to me otherwise.
return maybeAdapt(vh); | ||
// do not check for atomicity now -- atomicity will be checked on the accessed array object | ||
// a sharper var handle will be obtained (dynamically) from flatArrayElementHandleFor(Object) | ||
return maybeAdapt(new VarHandleReferences.Array(aoffset, ashift, arrayClass)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, now it is crucial for us to benchmark these var handles - I suspect they are very sensitive to profile pollutions.
@@ -433,7 +438,7 @@ final class VarHandle$Type$s { | |||
final CheckedType checkedFieldType; | |||
#end[Object] | |||
#if[FlatValue] | |||
final int layout; | |||
final int layout; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra spaces
@@ -25,8 +25,7 @@ | |||
/* | |||
* @test | |||
* @enablePreview | |||
* @run junit/othervm NullRestrictedArraysTest | |||
* @run junit/othervm -XX:-UseArrayFlattening NullRestrictedArraysTest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove the no flattening configurations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If array flattening is disabled, but field flattening is enabled, we might run into situation where the new flat array primitive fails -- that primitive requires same level of flattening for both arrays and fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've now re-enabled this combo -- but disabled both array flattening and nullable flattening.
Co-authored-by: Chen Liang <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
This PR is an attempt to put var handle support for flat values on a more solid footing. Some more notes in a comment below.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/valhalla.git pull/1402/head:pull/1402
$ git checkout pull/1402
Update a local copy of the PR:
$ git checkout pull/1402
$ git pull https://git.openjdk.org/valhalla.git pull/1402/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1402
View PR using the GUI difftool:
$ git pr show -t 1402
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/valhalla/pull/1402.diff
Using Webrev
Link to Webrev Comment