Skip to content

Commit 4a7f724

Browse files
committed
Improve CPython isolate handling and logcat setup
Ensures logcat forwarding is idempotent across Dart isolate restarts and avoids finalizing the CPython interpreter between runs to prevent native crashes. Also refines isolate management to prevent killing isolates that may leave the interpreter in a bad state, and improves error handling for logcat forwarding setup.
1 parent 524ce73 commit 4a7f724

File tree

1 file changed

+27
-14
lines changed

1 file changed

+27
-14
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ import 'gen.dart';
1111
export 'gen.dart';
1212

1313
CPython? _cpython;
14+
String? _logcatForwardingError;
1415
const _logcatInitScript = r'''
1516
import sys, logging
17+
18+
# Make this init idempotent across Dart isolate restarts.
19+
if getattr(sys, "__serious_python_logcat_configured__", False):
20+
raise SystemExit
21+
sys.__serious_python_logcat_configured__ = True
22+
1623
from ctypes import cdll
1724
liblog = cdll.LoadLibrary("liblog.so")
1825
ANDROID_LOG_INFO = 4
@@ -73,19 +80,23 @@ Future<String> runPythonProgramFFI(bool sync, String dynamicLibPath,
7380
final receivePort = ReceivePort();
7481
if (sync) {
7582
// sync run
76-
return await runPythonProgramInIsolate(
77-
[receivePort.sendPort, dynamicLibPath, pythonProgramPath, script]);
83+
try {
84+
return await runPythonProgramInIsolate(
85+
[receivePort.sendPort, dynamicLibPath, pythonProgramPath, script]);
86+
} finally {
87+
receivePort.close();
88+
}
7889
} else {
79-
var completer = Completer<String>();
8090
// async run
81-
final isolate = await Isolate.spawn(runPythonProgramInIsolate,
91+
//
92+
// IMPORTANT: do not `isolate.kill()` here. Killing the isolate can abort the
93+
// underlying OS thread while it still interacts with CPython, leaving the
94+
// interpreter/GIL in a bad state for subsequent runs.
95+
await Isolate.spawn(runPythonProgramInIsolate,
8296
[receivePort.sendPort, dynamicLibPath, pythonProgramPath, script]);
83-
receivePort.listen((message) {
84-
receivePort.close();
85-
isolate.kill();
86-
completer.complete(message);
87-
});
88-
return completer.future;
97+
final message = await receivePort.first;
98+
receivePort.close();
99+
return message as String;
89100
}
90101
}
91102

@@ -146,9 +157,9 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
146157
return "";
147158
});
148159
} finally {
149-
// Finalize interpreter so subsequent runs start clean and GIL is free.
150-
_finalizeInterpreter(cpython);
151-
_cpython = null;
160+
// Keep interpreter alive between runs. Finalizing + re-initializing the
161+
// interpreter is not reliably supported and has caused native crashes
162+
// (e.g. during _ctypes re-import) on Android.
152163
}
153164

154165
sendPort.send(result);
@@ -208,9 +219,11 @@ String? _setupLogcatForwarding(CPython cpython) {
208219
malloc.free(setupPtr);
209220

210221
if (result != 0) {
211-
return getPythonError(cpython);
222+
_logcatForwardingError = getPythonError(cpython);
223+
return _logcatForwardingError;
212224
}
213225

214226
_debug("logcat forwarding configured");
227+
_logcatForwardingError = null;
215228
return null;
216229
}

0 commit comments

Comments
 (0)