@@ -783,22 +783,33 @@ void runBackgroundGCTask(PythonContext context) {
783
783
* extensions - this can only be loaded once per process.
784
784
*/
785
785
private static final AtomicBoolean nativeCAPILoaded = new AtomicBoolean ();
786
- private static final AtomicBoolean warnedSecondContexWithNativeCAPI = new AtomicBoolean ();
787
786
788
787
private Runnable nativeFinalizerRunnable ;
789
788
private Thread nativeFinalizerShutdownHook ;
790
789
791
790
@ TruffleBoundary
792
- public static CApiContext ensureCapiWasLoaded () {
791
+ public static CApiContext ensureCapiWasLoaded (String reason ) {
793
792
try {
794
- return CApiContext .ensureCapiWasLoaded (null , PythonContext .get (null ), T_EMPTY_STRING , T_EMPTY_STRING );
793
+ return CApiContext .ensureCapiWasLoaded (null , PythonContext .get (null ), T_EMPTY_STRING , T_EMPTY_STRING , reason );
795
794
} catch (Exception e ) {
796
795
throw CompilerDirectives .shouldNotReachHere (e );
797
796
}
798
797
}
799
798
799
+ @ TruffleBoundary
800
+ public static Object ensureCApiLLVMLibrary (PythonContext context ) throws IOException , ImportException , ApiInitException {
801
+ CApiContext cApiContext = ensureCapiWasLoaded (null , context , T_EMPTY_STRING , T_EMPTY_STRING , " load LLVM library (this is an internal bug)" );
802
+ return cApiContext .getLLVMLibrary ();
803
+ }
804
+
800
805
@ TruffleBoundary
801
806
public static CApiContext ensureCapiWasLoaded (Node node , PythonContext context , TruffleString name , TruffleString path ) throws IOException , ImportException , ApiInitException {
807
+ return ensureCapiWasLoaded (node , context , name , path , null );
808
+ }
809
+
810
+ @ TruffleBoundary
811
+ public static CApiContext ensureCapiWasLoaded (Node node , PythonContext context , TruffleString name , TruffleString path , String reason ) throws IOException , ImportException , ApiInitException {
812
+ assert PythonContext .get (null ).ownsGil (); // unsafe lazy initialization
802
813
if (!context .hasCApiContext ()) {
803
814
Env env = context .getEnv ();
804
815
InteropLibrary U = InteropLibrary .getUncached ();
@@ -809,17 +820,25 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
809
820
TruffleFile capiFile = homePath .resolve (libName ).getCanonicalFile (LinkOption .NOFOLLOW_LINKS );
810
821
try {
811
822
SourceBuilder capiSrcBuilder ;
812
- final boolean useNative ;
813
- if (PythonOptions .NativeModules .getValue (env .getOptions ())) {
814
- useNative = nativeCAPILoaded .compareAndSet (false , true );
815
- if (!useNative && warnedSecondContexWithNativeCAPI .compareAndSet (false , true )) {
816
- LOGGER .warning ("GraalPy option 'NativeModules' is set to true, " +
817
- "but only one context in the process can use native modules, " +
818
- "second and other contexts fallback to NativeModules=false and " +
819
- "will use LLVM bitcode execution via GraalVM LLVM." );
823
+ final boolean useNative = PythonOptions .NativeModules .getValue (env .getOptions ());
824
+ if (useNative ) {
825
+ boolean canUseNative = nativeCAPILoaded .compareAndSet (false , true );
826
+ if (!canUseNative ) {
827
+ String actualReason = "initialize native extensions support" ;
828
+ if (reason != null ) {
829
+ actualReason = reason ;
830
+ } else if (name != null && path != null ) {
831
+ actualReason = String .format ("load a native module '%s' from path '%s'" , name .toJavaStringUncached (), path .toJavaStringUncached ());
832
+ }
833
+ throw new ApiInitException (TruffleString .fromJavaStringUncached (
834
+ String .format ("Option python.NativeModules is set to 'true' and a second GraalPy context attempted to %s. " +
835
+ "GraalPy currently only supports loading and using native extensions from one context when python.NativeModules is enabled. " +
836
+ "To resolve this issue, ensure that native extensions are used only in one GraalPy context per process or" +
837
+ "set python.NativeModules to 'false' to run native extensions in LLVM mode, which is recommended only " +
838
+ "for extensions included in the Python standard library. Running a 3rd party extension in LLVM mode requires " +
839
+ "a custom build of the extension and is generally discouraged due to compatibility reasons." , actualReason ),
840
+ PythonUtils .TS_ENCODING ));
820
841
}
821
- } else {
822
- useNative = false ;
823
842
}
824
843
if (useNative ) {
825
844
context .ensureNFILanguage (node , "NativeModules" , "true" );
0 commit comments