Skip to content
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

8301578: Perform output outside synchronization in Module.class #12193

Closed
wants to merge 11 commits into from

Conversation

minborg
Copy link
Contributor

@minborg minborg commented Jan 25, 2023

This PR proposed to reduce contention in synchronized methods mainly by doing I/O operations outside synch blocks.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8301578: Perform output outside synchronization in Module.class

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/12193/head:pull/12193
$ git checkout pull/12193

Update a local copy of the PR:
$ git checkout pull/12193
$ git pull https://git.openjdk.org/jdk pull/12193/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 12193

View PR using the GUI difftool:
$ git pr show -t 12193

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/12193.diff

@bridgekeeper
Copy link

bridgekeeper bot commented Jan 25, 2023

👋 Welcome back pminborg! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jan 25, 2023

@minborg The following label will be automatically applied to this pull request:

  • core-libs

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.

@openjdk openjdk bot added the core-libs core-libs-dev@openjdk.org label Jan 25, 2023
@minborg
Copy link
Contributor Author

minborg commented Jan 31, 2023

The potential performance improvement in determining if native access is enabled is a small part of the overall benchmarks in the LoopOverOfAddress benchmarks:

Baseline
Benchmark                                         Mode  Cnt  Score   Error  Units
LoopOverOfAddress.segment_loop_addr_size          avgt   30  0.563 ± 0.005  ms/op


PR
Benchmark                                         Mode  Cnt  Score   Error  Units
LoopOverOfAddress.segment_loop_addr_size          avgt   30  0.558 ± 0.002  ms/op

@minborg minborg marked this pull request as ready for review January 31, 2023 17:30
@openjdk openjdk bot added the rfr Pull request is ready for review label Jan 31, 2023
@mlbridge
Copy link

mlbridge bot commented Jan 31, 2023

}
}
}
}

private static boolean casEnableNativeAccess(Module target) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks okay but the naming is a bit misleading, maybe trySetEnableNativeAccess would be better, as this isn't an Unsafe/VarHandler compareAndSet. Also would be possible to add a comment to the method so it's consistent with the existing methods.

@AlanBateman
Copy link
Contributor

AlanBateman commented Jan 31, 2023

BTW: Is this PR meant by openjdk/jdk20 or openjdk/jdk? Does it really address a startup issue or not?

@dholmes-ora
Copy link
Member

I can't quite see the effect of the change here. You went from a synchronized instance method, to a non-synchronized static method which then immediately synchronizes on the passed in instance. ??

That aside I'm not sure what the synchronization issues are here - isn't setting the native access bit an idempotent operation?

@AlanBateman
Copy link
Contributor

I can't quite see the effect of the change here.

@minborg Have you used the wrong JBS issue by any chance? There is no information in the JBS issue, or here, that the proposed change fixes the reported startup regression after the JEP 434 integration and maybe this is a drive-by cleanup while investigating the startup issue?

}

private static boolean isNativeAccessEnabled(Module target) {
if (target.enableNativeAccess)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@minborg It'd seem a bit easier to implement using VarHandle and not having to use synchronized? Is the reason that you don't want to init VarHandle in this class? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bootstrap problem. We cannot use AtomicBoolean, VarHandle or Unsafe here.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t Unsafe already be loaded at this point?

@minborg minborg changed the title 8300917: Regression 2x and bimodal startup on Mac aarch64 in b27 8301578: Perform output outside synchronization in Module.class Feb 1, 2023
@minborg
Copy link
Contributor Author

minborg commented Feb 1, 2023

Let me take a step back here and explain the reason why I think this PR is worth looking at. Firstly, I think the argument that this might not solve https://bugs.openjdk.org/browse/JDK-8300917 is perfectly valid. Therefore, I have filed a separate issue and linked this PR to this. Thanks for pointing this out. This PR is more like a drive-by cleanup as mentioned.

Back to the contents of the PR. My fear (which may or may not be substantiated) is that if we perform operations via the relatively slow System.err.printf, while being in a synchronization block, we are exposed to a number of potential problems:

  • Another thread might be performing System.err operations which will be under a separate lock/sync
  • Other threads are calling ensureNativeAccess for the same module
  • A "storm of calls" during bootstrap
  • Combinations of any of the above

Again, this might not be the case in reality but I think removing the risk is worthwhile.

Comment on lines 303 to 316
} else {
if (trySetEnableNativeAccess(target)) {
// warn and set flag, so that only one warning is reported per module
String cls = owner.getName();
String mtd = cls + "::" + methodName;
String mod = isNamed() ? "module " + getName() : "the unnamed module";
String modflag = isNamed() ? getName() : "ALL-UNNAMED";
System.err.printf("""
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);

// set the flag
target.enableNativeAccess = true;
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else and if blocks can be merged:

Suggested change
} else {
if (trySetEnableNativeAccess(target)) {
// warn and set flag, so that only one warning is reported per module
String cls = owner.getName();
String mtd = cls + "::" + methodName;
String mod = isNamed() ? "module " + getName() : "the unnamed module";
String modflag = isNamed() ? getName() : "ALL-UNNAMED";
System.err.printf("""
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);
// set the flag
target.enableNativeAccess = true;
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);
}
}
} else if (trySetEnableNativeAccess(target)) {
// warn and set flag, so that only one warning is reported per module
String cls = owner.getName();
String mtd = cls + "::" + methodName;
String mod = isNamed() ? "module " + getName() : "the unnamed module";
String modflag = isNamed() ? getName() : "ALL-UNNAMED";
System.err.printf("""
WARNING: A restricted method in %s has been called
WARNING: %s has been called by %s
WARNING: Use --enable-native-access=%s to avoid a warning for this module
%n""", cls, mtd, mod, modflag);
}

}

private static boolean isNativeAccessEnabled(Module target) {
if (target.enableNativeAccess)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t Unsafe already be loaded at this point?

@minborg
Copy link
Contributor Author

minborg commented Feb 9, 2023

By putting Unsafe in an inner class, we are able to avoid circular initializer dependencies and I have been able to rewrite enableNativeAccess access so it is completely lock-free.

// Accessing this variable is made through Unsafe in order to use the
// memory semantics that preserves ordering and visibility across threads.
//
// Used reflectively via Unsafe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume L119-120 can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reson for the L119-120 comments is to make it easier to understand why we are declaring a variable that does not appear to be used. But maybe there is a better way to do that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment at L117-118 already explains that it's accessed with Unsafe so I don't think we need any more than that. The IDE may suggest the field is unused but if anyone removes it then it will break the JDK build/startup. Same thing many other areas where Unsafe or VarHandles are used. So I don't think we should be too concerned about and the comments at L116-118 are more than enough.

@@ -124,8 +129,7 @@ public final class Module implements AnnotatedElement {
Module(ModuleLayer layer,
ClassLoader loader,
ModuleDescriptor descriptor,
URI uri)
{
URI uri) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a spurious edit, not sure if you meant to change it.

*
* @throws SecurityException
* If denied by the security manager
* @throws SecurityException If denied by the security manager
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception messages are aligned under the exceptions in this class so I assume you didn't mean to change this.

@@ -229,7 +230,7 @@ public ModuleDescriptor getDescriptor() {
/**
* Returns the module layer that contains this module or {@code null} if
* this module is not in a module layer.
*
* <p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break up the first paragraph, I don't think this PR should be changing that.

private AccessHolder() {}

private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final long FIELD_OFFSET = UNSAFE.objectFieldOffset(Module.class, "enableNativeAccess");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Unsafe and CAS'ing the enableNativeAccess field looks okay. The name "AccessHolder" looks too general here as there is a lot of access going on in this class, maybe NativeAccessHolder will work.

It would be good if you could try to keep changes to this code consistent with the existing style if possible. In this case, the other inner class uses /** .. */ style comment for the class description. I think would I put the constants at the top of the this class.

Copy link
Contributor

@AlanBateman AlanBateman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good cleanup overall.

// Accessing this variable is made through Unsafe in order to use the
// memory semantics that preserves ordering and visibility across threads.
//
// Used reflectively via Unsafe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment at L117-118 already explains that it's accessed with Unsafe so I don't think we need any more than that. The IDE may suggest the field is unused but if anyone removes it then it will break the JDK build/startup. Same thing many other areas where Unsafe or VarHandles are used. So I don't think we should be too concerned about and the comments at L116-118 are more than enough.

* This class is used to be able to bootstrap without using Unsafe
* in the outer Module class as that would create a circular initializer dependency.
*/
private static final class EnableNativeAccessHandler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this has been renamed EnableNativeAccessHandler, maybe drop "Handler" as it just sets the enableNativeAccess bit, nothing more.

@openjdk
Copy link

openjdk bot commented Feb 10, 2023

@minborg 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:

8301578: Perform output outside synchronization in Module.class

Reviewed-by: alanb

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 270 new commits pushed to the master branch:

  • 1c7b09b: 8302069: javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java update
  • 837d464: 8302125: Make G1 full gc abort the VM after failing VerifyDuringGC verification
  • 723433d: 8302117: IgnoreUnrecognizedVMOptions flag causes failure in ArchiveHeapTestClass
  • e245620: 8293198: [vectorapi] Improve the implementation of VectorMask.indexInRange()
  • b814cfc: 8178806: Better exception logging in crypto code
  • 8c87a67: 8245654: Add Certigna Root CA
  • 97d0c87: 8302109: Trivial fixes to btree tests
  • 0aeebee: 8301988: VerifyLiveClosure::verify_liveness asserts on bad pointers outside heap
  • 4815566: 8228604: StackMapFrames are missing from redefined class bytes of retransformed classes
  • 5147969: 8272288: Funky multiresolution image breaks graphics context
  • ... and 260 more: https://git.openjdk.org/jdk/compare/a5d8e12872d9de399fa97b33896635d101b71372...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Feb 10, 2023
@minborg
Copy link
Contributor Author

minborg commented Feb 10, 2023

/integrate

@openjdk
Copy link

openjdk bot commented Feb 10, 2023

Going to push as commit c25b4f4.
Since your change was applied there have been 272 commits pushed to the master branch:

  • 5830c03: 8302004: InlineTree should consult replay file in release build
  • c8ace48: 8301072: Replace NULL with nullptr in share/oops/
  • 1c7b09b: 8302069: javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java update
  • 837d464: 8302125: Make G1 full gc abort the VM after failing VerifyDuringGC verification
  • 723433d: 8302117: IgnoreUnrecognizedVMOptions flag causes failure in ArchiveHeapTestClass
  • e245620: 8293198: [vectorapi] Improve the implementation of VectorMask.indexInRange()
  • b814cfc: 8178806: Better exception logging in crypto code
  • 8c87a67: 8245654: Add Certigna Root CA
  • 97d0c87: 8302109: Trivial fixes to btree tests
  • 0aeebee: 8301988: VerifyLiveClosure::verify_liveness asserts on bad pointers outside heap
  • ... and 262 more: https://git.openjdk.org/jdk/compare/a5d8e12872d9de399fa97b33896635d101b71372...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Feb 10, 2023
@openjdk openjdk bot closed this Feb 10, 2023
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Feb 10, 2023
@openjdk
Copy link

openjdk bot commented Feb 10, 2023

@minborg Pushed as commit c25b4f4.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core-libs core-libs-dev@openjdk.org integrated Pull request has been integrated
5 participants