diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index 6f4da2219c1..aafe1b89a4b 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,7 @@ import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.BootLoader; import jdk.internal.loader.ClassLoaders; import jdk.internal.misc.CDS; +import jdk.internal.misc.Unsafe; import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.ModuleLoaderMap; import jdk.internal.module.ServicesCatalog; @@ -113,6 +114,8 @@ public final class Module implements AnnotatedElement { private final ModuleDescriptor descriptor; // true, if this module allows restricted native access + // Accessing this variable is made through Unsafe in order to use the + // memory semantics that preserves ordering and visibility across threads. @Stable private boolean enableNativeAccess; @@ -258,8 +261,8 @@ public final class Module implements AnnotatedElement { /** * Update this module to allow access to restricted methods. */ - synchronized Module implAddEnableNativeAccess() { - enableNativeAccess = true; + Module implAddEnableNativeAccess() { + EnableNativeAccess.trySetEnableNativeAccess(this); return this; } @@ -267,15 +270,34 @@ public final class Module implements AnnotatedElement { * Returns {@code true} if this module can access * restricted methods. * - * @since 20 - * * @return {@code true} if this module can access restricted methods. + * @since 20 */ - @PreviewFeature(feature=PreviewFeature.Feature.FOREIGN) + @PreviewFeature(feature = PreviewFeature.Feature.FOREIGN) public boolean isNativeAccessEnabled() { Module target = moduleForNativeAccess(); - synchronized(target) { - return target.enableNativeAccess; + return EnableNativeAccess.isNativeAccessEnabled(target); + } + + /** + * 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 EnableNativeAccess { + + private EnableNativeAccess() {} + + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final long FIELD_OFFSET = UNSAFE.objectFieldOffset(Module.class, "enableNativeAccess"); + + private static boolean isNativeAccessEnabled(Module target) { + return UNSAFE.getBooleanVolatile(target, FIELD_OFFSET); + } + + // Atomically sets enableNativeAccess if not already set + // returning if the value was updated + private static boolean trySetEnableNativeAccess(Module target) { + return UNSAFE.compareAndSetBoolean(target, FIELD_OFFSET, false, true); } } @@ -289,46 +311,30 @@ public final class Module implements AnnotatedElement { void ensureNativeAccess(Class owner, String methodName) { // The target module whose enableNativeAccess flag is ensured Module target = moduleForNativeAccess(); - // racy read of the enable native access flag - boolean isNativeAccessEnabled = target.enableNativeAccess; - if (!isNativeAccessEnabled) { - synchronized (target) { - // safe read of the enableNativeAccess of the target module - isNativeAccessEnabled = target.enableNativeAccess; - - // check again with the safely read flag - if (isNativeAccessEnabled) { - // another thread beat us to it - nothing to do - return; - } else if (ModuleBootstrap.hasEnableNativeAccessFlag()) { - throw new IllegalCallerException("Illegal native access from: " + this); - } else { - // 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(""" + if (!EnableNativeAccess.isNativeAccessEnabled(target)) { + if (ModuleBootstrap.hasEnableNativeAccessFlag()) { + throw new IllegalCallerException("Illegal native access from: " + this); + } + if (EnableNativeAccess.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; - } } } } - /** * Update all unnamed modules to allow access to restricted methods. */ static void implAddEnableNativeAccessToAllUnnamed() { - synchronized (ALL_UNNAMED_MODULE) { - ALL_UNNAMED_MODULE.enableNativeAccess = true; - } + EnableNativeAccess.trySetEnableNativeAccess(ALL_UNNAMED_MODULE); } // --