Skip to content

Commit 2347501

Browse files
committed
Refactor GIL management for Python interpreter
Replaces manual thread state management with PyGILState_Ensure and PyGILState_Release for safer and more robust GIL handling across isolates. Removes global variables and simplifies initialization logic to avoid crashes on repeated interpreter use.
1 parent fb39db7 commit 2347501

File tree

1 file changed

+12
-24
lines changed

1 file changed

+12
-24
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ 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;
17-
Pointer<PyThreadState>? _mainThreadState;
1814

1915
void _pyLog(String msg) {
2016
debugPrint("[PY] ${DateTime.now().toIso8601String()} $msg");
@@ -60,28 +56,21 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
6056
_pyLog("script provided: ${script.isNotEmpty} length=${script.length}");
6157

6258
final cpython = getCPython(dynamicLibPath);
63-
if (!_pythonInitialized) {
59+
final alreadyInit = cpython.Py_IsInitialized() != 0;
60+
_pyLog("Py_IsInitialized: $alreadyInit");
61+
if (!alreadyInit) {
6462
_pyLog("calling Py_Initialize()");
6563
cpython.Py_Initialize();
66-
// Release the GIL from the init thread so other threads can reacquire it.
67-
_mainThreadState = cpython.PyEval_SaveThread();
68-
_pythonInitialized = true;
69-
_pyLog(
70-
"after Py_Initialize(), saved thread state: ${_mainThreadState?.address.toRadixString(16)}");
64+
_pyLog("after Py_Initialize()");
7165
} else {
7266
_pyLog("Python already initialized; reusing interpreter");
7367
}
7468

7569
var result = "";
7670

77-
// Reacquire the GIL using the saved main thread state for this process.
78-
final mainState = _mainThreadState;
79-
if (mainState == null) {
80-
throw StateError("Python main thread state is null after initialization");
81-
}
82-
83-
_pyLog("restoring thread state ${mainState.address.toRadixString(16)}");
84-
cpython.PyEval_RestoreThread(mainState);
71+
// Ensure the calling thread owns the GIL; this is safe across isolates.
72+
final gilState = cpython.PyGILState_Ensure();
73+
_pyLog("PyGILState_Ensure -> $gilState");
8574

8675
try {
8776
if (script != "") {
@@ -98,20 +87,19 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
9887
// run program
9988
final moduleNamePtr = programModuleName.toNativeUtf8();
10089
_pyLog("calling PyImport_ImportModule for $programModuleName");
101-
var modulePtr =
102-
cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
90+
var modulePtr = cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
10391
if (modulePtr == nullptr) {
10492
result = getPythonError(cpython);
10593
} else {
106-
_pyLog("PyImport_ImportModule returned ${modulePtr.address.toRadixString(16)}");
94+
_pyLog(
95+
"PyImport_ImportModule returned ${modulePtr.address.toRadixString(16)}");
10796
}
10897
malloc.free(moduleNamePtr);
10998
}
11099
} finally {
111100
// Drop the GIL again so subsequent runs/threads can re-acquire it cleanly.
112-
_mainThreadState = cpython.PyEval_SaveThread();
113-
_pyLog(
114-
"saved thread state after run: ${_mainThreadState?.address.toRadixString(16)}");
101+
_pyLog("PyGILState_Release($gilState)");
102+
cpython.PyGILState_Release(gilState);
115103
}
116104

117105
// cpython.Py_Finalize();

0 commit comments

Comments
 (0)