-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
JDK-8310913 Move ReferencedKeyMap to jdk.internal so it may be shared #14684
Conversation
👋 Welcome back jlaskey! A progress list of the required criteria for merging this PR into |
@JimLaskey The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
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 fine to me. Good to see this class is being shared, as this can be used for refactoring the locale-related caches (#14404).
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES. | ||
* Do not rely on its availability. | ||
*/ |
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.
Now that this is shared by other components, do we still need this warning?
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.
True that.
@JimLaskey 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 |
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.
LGTM
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.
MethodType's ConcurrentWeakInternSet
uses NativeReferenceQueue
which uses native monitors (that was added for virtual threads). This change uses the reference queue using j.u.c locks. Has you confirmed that this is not an issue? Or ReferencedKeySet
can take a parameter to specify using the native reference queue?
In ReferencedKey::equals
method, can use Reference::refersTo
instead of Objects.equals
.
@mlchung I missed the use of newNativeReferenceQueue. This change has been sitting in my queue for a few years, I will adapt to the new reality. Thank you for pointing it out. |
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.
WeakReferenceKey::equals
and SoftReferenceKey::equals
:
Instead of return Objects.equals(get(), obj);
, suggest to do:
return refersTo(obj);
@mlchung Not sure I catch the nuance. |
Using |
@RogerRiggs |
src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java
Outdated
Show resolved
Hide resolved
The comparison of the unwrapped key using |
thanks. It may worth adding a comment about equality comparison and can't use |
src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java
Outdated
Show resolved
Hide resolved
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. Thanks for the update. Nit: the test can use the 2-arg factory method.
src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/jdk/internal/util/ReferencedKeyMap.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/jdk/internal/util/ReferencedKeySet.java
Outdated
Show resolved
Hide resolved
Added intern with UnaryOperator<T> interning function to prepare key before adding to set.
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.
Thank you for the updates.
/integrate |
Going to push as commit 6af0af5.
Your commit was automatically rebased without conflicts. |
@JimLaskey Pushed as commit 6af0af5. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
|
||
@Override | ||
public boolean add(T e) { | ||
return intern(e) == null; |
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 line is wrong, as intern(…)
will never return null
.
This is the closest to the correct implementation,
return intern(e) == null; | |
return !contains(e) & intern(e) == e; |
but may incorrectly return true
in case of the following data race, assuming t1e == t2e
:
- Thread 1 calls
contains(t1e)
and getsfalse
. - Thread 2 calls
intern(t2e)
and successfully addst2e
. - Thread 1 calls
intern(t1e)
and gets backt2e
, which is==
tot1e
. - Thread 1 returns
true
, even though it was Thread 2 which modified theReferencedKeySet
.
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.
Good catch. Your solution might be correct but I think !contains(e)
is redundant since that is how intern starts out.
static <T> T intern(ReferencedKeyMap<T, ReferenceKey<T>> setMap, T key, UnaryOperator<T> interner) {
T value = existingKey(setMap, key);
if (value != null) {
return value;
}
key = interner.apply(key);
return internKey(setMap, key);
}
Agree? So changing to return intern(e) == e;
should be sufficient.
The other aspect of this is that internKey
uses putIfAbsent
which should prevent the race (assuming ConcurrentHashMap
).
/**
* Attempt to add key to map.
*
* @param setMap {@link ReferencedKeyMap} where interning takes place
* @param key key to add
*
* @param <T> type of key
*
* @return the old key instance if found otherwise the new key instance
*/
private static <T> T internKey(ReferencedKeyMap<T, ReferenceKey<T>> setMap, T key) {
ReferenceKey<T> entryKey = setMap.entryKey(key);
T interned;
do {
setMap.removeStaleReferences();
ReferenceKey<T> existing = setMap.map.putIfAbsent(entryKey, entryKey);
if (existing == null) {
return key;
} else {
// If {@code putIfAbsent} returns non-null then was actually a
// {@code replace} and older key was used. In that case the new
// key was not used and the reference marked stale.
interned = existing.get();
if (interned != null) {
entryKey.unused();
}
}
} while (interned == null);
return interned;
}
Agree? Anyone else want to pipe in?
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.
ok, intern(e) == e
is sufficient.
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.
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 intern(e) == e
is (mostly) sufficient, it will return a false positive when add(e)
is called and e
is already present in the map, the correctest implementation would be some internal API in ReferencedKeyMap
for implementing ReferencedKeySet::add
.
java.lang.runtime.ReferencedKeyMap was introduced to provide a concurrent caching scheme for Carrier objects. The technique used is generally useful for a variety of caching schemes and is being moved to be shared in other parts of the jdk. The MethodType interning case is one example.
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/14684/head:pull/14684
$ git checkout pull/14684
Update a local copy of the PR:
$ git checkout pull/14684
$ git pull https://git.openjdk.org/jdk.git pull/14684/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 14684
View PR using the GUI difftool:
$ git pr show -t 14684
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/14684.diff
Webrev
Link to Webrev Comment