Skip to content

Commit 74a5136

Browse files
committed
Error on second context trying to load native extensions
1 parent 388de48 commit 74a5136

File tree

5 files changed

+42
-25
lines changed

5 files changed

+42
-25
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/nodes/MemMoveNodeTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class MemMoveNodeTests {
6565
public void setUp() {
6666
PythonTests.enterContext();
6767
this.gil = GilNode.uncachedAcquire();
68-
CApiContext.ensureCapiWasLoaded();
68+
CApiContext.ensureCapiWasLoaded("internal");
6969
}
7070

7171
@After

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,7 +838,7 @@ abstract static class StorageToNative extends PythonUnaryBuiltinNode {
838838
@Specialization
839839
@TruffleBoundary
840840
Object toNative(PArray array) {
841-
CApiContext.ensureCapiWasLoaded();
841+
CApiContext.ensureCapiWasLoaded("internal API");
842842
NativeSequenceStorage newStorage = ToNativeStorageNode.executeUncached(array.getSequenceStorage(), true);
843843
array.setSequenceStorage(newStorage);
844844
return array;
@@ -847,7 +847,7 @@ Object toNative(PArray array) {
847847
@Specialization
848848
@TruffleBoundary
849849
Object toNative(PSequence sequence) {
850-
CApiContext.ensureCapiWasLoaded();
850+
CApiContext.ensureCapiWasLoaded("internal API");
851851
NativeSequenceStorage newStorage = ToNativeStorageNode.executeUncached(sequence.getSequenceStorage(), sequence instanceof PBytesLike);
852852
sequence.setSequenceStorage(newStorage);
853853
return sequence;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
import static com.oracle.graal.python.nodes.StringLiterals.J_EMPTY_STRING;
7373
import static com.oracle.graal.python.nodes.StringLiterals.J_LLVM_LANGUAGE;
7474
import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE;
75-
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
7675
import static com.oracle.graal.python.nodes.StringLiterals.T_LPAREN;
7776
import static com.oracle.graal.python.runtime.PosixConstants.RTLD_GLOBAL;
7877
import static com.oracle.graal.python.runtime.PosixConstants.RTLD_LOCAL;
@@ -286,8 +285,8 @@ public void postInitialize(Python3Core core) {
286285
}
287286
} else {
288287
try {
289-
CApiContext cApiContext = CApiContext.ensureCapiWasLoaded(null, context, T_EMPTY_STRING, T_EMPTY_STRING);
290-
handle = new DLHandler(cApiContext.getLLVMLibrary(), 0, J_EMPTY_STRING, true);
288+
Object llvmLibrary = CApiContext.ensureCApiLLVMLibrary(context);
289+
handle = new DLHandler(llvmLibrary, 0, J_EMPTY_STRING, true);
291290
setCtypeLLVMHelpers(this, handle);
292291
} catch (ApiInitException e) {
293292
throw e.reraise(null, null, PConstructAndRaiseNode.Lazy.getUncached());
@@ -715,8 +714,7 @@ private static Object load(PythonContext context, String src, String name) {
715714
protected static Object loadLLVMLibrary(PythonContext context, Node nodeForRaise, TruffleString path) throws ImportException, ApiInitException, IOException {
716715
context.ensureLLVMLanguage(nodeForRaise);
717716
if (path.isEmpty()) {
718-
CApiContext cApiContext = CApiContext.ensureCapiWasLoaded(null, context, T_EMPTY_STRING, T_EMPTY_STRING);
719-
return cApiContext.getLLVMLibrary();
717+
return CApiContext.ensureCApiLLVMLibrary(context);
720718
}
721719
Source loadSrc = Source.newBuilder(J_LLVM_LANGUAGE, context.getPublicTruffleFileRelaxed(path)).build();
722720
return context.getEnv().parseInternal(loadSrc).call();
@@ -747,7 +745,7 @@ static Object py_dl_open(VirtualFrame frame, PythonModule self, TruffleString na
747745
}
748746

749747
// The loaded library can link against libpython, so we have to make sure it is loaded
750-
CApiContext.ensureCapiWasLoaded();
748+
CApiContext.ensureCapiWasLoaded("support ctypes module");
751749

752750
int mode = m != Integer.MIN_VALUE ? m : RTLD_LOCAL.getValueIfDefined();
753751
mode |= RTLD_NOW.getValueIfDefined();
@@ -1703,7 +1701,7 @@ protected abstract static class PyINCREFNode extends PythonUnaryBuiltinNode {
17031701
@Specialization
17041702
@TruffleBoundary
17051703
static Object doGeneric(Object arg) {
1706-
CApiContext.ensureCapiWasLoaded();
1704+
CApiContext.ensureCapiWasLoaded("support ctypes module");
17071705
CApiTransitions.PythonToNativeNewRefNode.executeUncached(arg);
17081706
return arg;
17091707
}
@@ -1716,7 +1714,7 @@ protected abstract static class PyDECREFNode extends PythonUnaryBuiltinNode {
17161714
@Specialization
17171715
@TruffleBoundary
17181716
static Object doGeneric(Object arg) {
1719-
CApiContext.ensureCapiWasLoaded();
1717+
CApiContext.ensureCapiWasLoaded("support ctypes module");
17201718
Object nativePointer = CApiTransitions.PythonToNativeNode.executeUncached(arg);
17211719
CExtNodes.XDecRefPointerNode.executeUncached(nativePointer);
17221720
return arg;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -783,22 +783,33 @@ void runBackgroundGCTask(PythonContext context) {
783783
* extensions - this can only be loaded once per process.
784784
*/
785785
private static final AtomicBoolean nativeCAPILoaded = new AtomicBoolean();
786-
private static final AtomicBoolean warnedSecondContexWithNativeCAPI = new AtomicBoolean();
787786

788787
private Runnable nativeFinalizerRunnable;
789788
private Thread nativeFinalizerShutdownHook;
790789

791790
@TruffleBoundary
792-
public static CApiContext ensureCapiWasLoaded() {
791+
public static CApiContext ensureCapiWasLoaded(String reason) {
793792
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);
795794
} catch (Exception e) {
796795
throw CompilerDirectives.shouldNotReachHere(e);
797796
}
798797
}
799798

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+
800805
@TruffleBoundary
801806
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
802813
if (!context.hasCApiContext()) {
803814
Env env = context.getEnv();
804815
InteropLibrary U = InteropLibrary.getUncached();
@@ -809,17 +820,25 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
809820
TruffleFile capiFile = homePath.resolve(libName).getCanonicalFile(LinkOption.NOFOLLOW_LINKS);
810821
try {
811822
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));
820841
}
821-
} else {
822-
useNative = false;
823842
}
824843
if (useNative) {
825844
context.ensureNFILanguage(node, "NativeModules", "true");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ static Object doWithoutContext(NativeCAPISymbol symbol, Object[] args,
860860
PythonContext pythonContext = PythonContext.get(inliningTarget);
861861
if (!pythonContext.hasCApiContext()) {
862862
CompilerDirectives.transferToInterpreterAndInvalidate();
863-
CApiContext.ensureCapiWasLoaded();
863+
CApiContext.ensureCapiWasLoaded("call internal native GraalPy function");
864864
}
865865
// TODO review EnsureTruffleStringNode with GR-37896
866866
Object callable = CApiContext.getNativeSymbol(inliningTarget, symbol);

0 commit comments

Comments
 (0)