Skip to content

Commit 3f4adc0

Browse files
committed
Ensure single CPython interpreter per process
Introduces a guard to prevent repeated initialization and finalization of the CPython interpreter, addressing crashes on second launch with CPython 3.12. Also ensures the current native thread is registered with the GIL when running Python code, improving stability when re-entering Python from new Dart isolates or threads.
1 parent 8d84d6f commit 3f4adc0

File tree

1 file changed

+35
-22
lines changed

1 file changed

+35
-22
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import 'gen.dart';
1111
export 'gen.dart';
1212

1313
CPython? _cpython;
14+
// Keep a single interpreter per process; repeated init/finalize of CPython 3.12
15+
// from an embedder is fragile and was crashing on second launch.
16+
bool _pythonInitialized = false;
1417

1518
CPython getCPython(String dynamicLibPath) {
1619
return _cpython ??= _cpython = CPython(DynamicLibrary.open(dynamicLibPath));
@@ -51,33 +54,43 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
5154
debugPrint("programModuleName: $programModuleName");
5255

5356
final cpython = getCPython(dynamicLibPath);
54-
cpython.Py_Initialize();
55-
debugPrint("after Py_Initialize()");
57+
if (!_pythonInitialized) {
58+
cpython.Py_Initialize();
59+
_pythonInitialized = true;
60+
debugPrint("after Py_Initialize()");
61+
} else {
62+
debugPrint("Python already initialized; reusing interpreter");
63+
}
5664

5765
var result = "";
58-
59-
if (script != "") {
60-
// run script
61-
final scriptPtr = script.toNativeUtf8();
62-
int sr = cpython.PyRun_SimpleString(scriptPtr.cast<Char>());
63-
debugPrint("PyRun_SimpleString for script result: $sr");
64-
malloc.free(scriptPtr);
65-
if (sr != 0) {
66-
result = getPythonError(cpython);
67-
}
68-
} else {
69-
// run program
70-
final moduleNamePtr = programModuleName.toNativeUtf8();
71-
var modulePtr = cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
72-
if (modulePtr == nullptr) {
73-
result = getPythonError(cpython);
66+
// Ensure the current native thread is registered with the GIL; this is
67+
// required if we re-enter Python from a new Dart isolate/thread.
68+
final gilState = cpython.PyGILState_Ensure();
69+
70+
try {
71+
if (script != "") {
72+
// run script
73+
final scriptPtr = script.toNativeUtf8();
74+
int sr = cpython.PyRun_SimpleString(scriptPtr.cast<Char>());
75+
debugPrint("PyRun_SimpleString for script result: $sr");
76+
malloc.free(scriptPtr);
77+
if (sr != 0) {
78+
result = getPythonError(cpython);
79+
}
80+
} else {
81+
// run program
82+
final moduleNamePtr = programModuleName.toNativeUtf8();
83+
var modulePtr =
84+
cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
85+
if (modulePtr == nullptr) {
86+
result = getPythonError(cpython);
87+
}
88+
malloc.free(moduleNamePtr);
7489
}
75-
malloc.free(moduleNamePtr);
90+
} finally {
91+
cpython.PyGILState_Release(gilState);
7692
}
7793

78-
cpython.Py_Finalize();
79-
debugPrint("after Py_Finalize()");
80-
8194
sendPort.send(result);
8295

8396
return result;

0 commit comments

Comments
 (0)