Skip to content

Commit 8633a01

Browse files
committed
Ensure GIL is held for all Python C API calls
Introduced a _withGIL helper to wrap all calls to the Python C API with GIL acquisition and release. This change improves thread safety and correctness when interacting with the Python interpreter from Dart, especially in isolate contexts.
1 parent 5f6c029 commit 8633a01

File tree

1 file changed

+49
-29
lines changed

1 file changed

+49
-29
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ void _debug(String message) {
4646
debugPrint("[serious_python] $message");
4747
}
4848

49+
T _withGIL<T>(CPython cpython, T Function() action) {
50+
final gil = cpython.PyGILState_Ensure();
51+
try {
52+
return action();
53+
} finally {
54+
cpython.PyGILState_Release(gil);
55+
}
56+
}
57+
4958
Future<String> runPythonProgramFFI(bool sync, String dynamicLibPath,
5059
String pythonProgramPath, String script) async {
5160
final receivePort = ReceivePort();
@@ -87,7 +96,7 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
8796
_debug("after Py_Initialize()");
8897
}
8998

90-
final logcatSetupError = _setupLogcatForwarding(cpython);
99+
final logcatSetupError = _withGIL(cpython, () => _setupLogcatForwarding(cpython));
91100
if (logcatSetupError != null) {
92101
sendPort.send(logcatSetupError);
93102
return logcatSetupError;
@@ -97,23 +106,31 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
97106

98107
if (script != "") {
99108
// run script
100-
_debug("Running script: $script");
101-
final scriptPtr = script.toNativeUtf8();
102-
int sr = cpython.PyRun_SimpleString(scriptPtr.cast<Char>());
103-
_debug("PyRun_SimpleString for script result: $sr");
104-
malloc.free(scriptPtr);
105-
if (sr != 0) {
106-
result = getPythonError(cpython);
107-
}
109+
result = _withGIL(cpython, () {
110+
_debug("Running script: $script");
111+
final scriptPtr = script.toNativeUtf8();
112+
int sr = cpython.PyRun_SimpleString(scriptPtr.cast<Char>());
113+
_debug("PyRun_SimpleString for script result: $sr");
114+
malloc.free(scriptPtr);
115+
if (sr != 0) {
116+
return getPythonError(cpython);
117+
}
118+
return "";
119+
});
108120
} else {
109121
// run program
110-
_debug("Running program module: $programModuleName");
111-
final moduleNamePtr = programModuleName.toNativeUtf8();
112-
var modulePtr = cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
113-
if (modulePtr == nullptr) {
114-
result = getPythonError(cpython);
115-
}
116-
malloc.free(moduleNamePtr);
122+
result = _withGIL(cpython, () {
123+
_debug("Running program module: $programModuleName");
124+
final moduleNamePtr = programModuleName.toNativeUtf8();
125+
var modulePtr = cpython.PyImport_ImportModule(moduleNamePtr.cast<Char>());
126+
if (modulePtr == nullptr) {
127+
final error = getPythonError(cpython);
128+
malloc.free(moduleNamePtr);
129+
return error;
130+
}
131+
malloc.free(moduleNamePtr);
132+
return "";
133+
});
117134
}
118135

119136
_debug("Python program finished");
@@ -125,38 +142,41 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
125142

126143
String getPythonError(CPython cpython) {
127144
// get error object
128-
var exPtr = cpython.PyErr_GetRaisedException();
145+
var exPtr = _withGIL(cpython, () => cpython.PyErr_GetRaisedException());
129146

130147
// use 'traceback' module to format exception
131148
final tracebackModuleNamePtr = "traceback".toNativeUtf8();
132-
var tracebackModulePtr =
133-
cpython.PyImport_ImportModule(tracebackModuleNamePtr.cast<Char>());
149+
var tracebackModulePtr = _withGIL(cpython,
150+
() => cpython.PyImport_ImportModule(tracebackModuleNamePtr.cast<Char>()));
134151
cpython.Py_DecRef(tracebackModuleNamePtr.cast());
135152

136153
if (tracebackModulePtr != nullptr) {
137154
//_debug("Traceback module loaded");
138155

139156
final formatFuncName = "format_exception".toNativeUtf8();
140-
final pFormatFunc = cpython.PyObject_GetAttrString(
141-
tracebackModulePtr, formatFuncName.cast());
157+
final pFormatFunc = _withGIL(
158+
cpython,
159+
() => cpython.PyObject_GetAttrString(
160+
tracebackModulePtr, formatFuncName.cast()));
142161
cpython.Py_DecRef(tracebackModuleNamePtr.cast());
143162

144163
if (pFormatFunc != nullptr && cpython.PyCallable_Check(pFormatFunc) != 0) {
145164
// call `traceback.format_exception()` method
146-
final pArgs = cpython.PyTuple_New(1);
147-
cpython.PyTuple_SetItem(pArgs, 0, exPtr);
165+
final pArgs = _withGIL(cpython, () => cpython.PyTuple_New(1));
166+
_withGIL(cpython, () => cpython.PyTuple_SetItem(pArgs, 0, exPtr));
148167

149168
// result is a list
150-
var listPtr = cpython.PyObject_CallObject(pFormatFunc, pArgs);
169+
var listPtr =
170+
_withGIL(cpython, () => cpython.PyObject_CallObject(pFormatFunc, pArgs));
151171

152172
// get and combine list items
153173
var exLines = [];
154-
var listSize = cpython.PyList_Size(listPtr);
174+
var listSize = _withGIL(cpython, () => cpython.PyList_Size(listPtr));
155175
for (var i = 0; i < listSize; i++) {
156-
var itemObj = cpython.PyList_GetItem(listPtr, i);
157-
var itemObjStr = cpython.PyObject_Str(itemObj);
158-
var s =
159-
cpython.PyUnicode_AsUTF8(itemObjStr).cast<Utf8>().toDartString();
176+
var itemObj = _withGIL(cpython, () => cpython.PyList_GetItem(listPtr, i));
177+
var itemObjStr = _withGIL(cpython, () => cpython.PyObject_Str(itemObj));
178+
var s = _withGIL(cpython,
179+
() => cpython.PyUnicode_AsUTF8(itemObjStr).cast<Utf8>().toDartString());
160180
exLines.add(s);
161181
}
162182
return exLines.join("");

0 commit comments

Comments
 (0)