Skip to content

Commit 9327e4d

Browse files
committed
Serialize Python runs and improve async execution
Introduces a queue to serialize Python execution requests, preventing concurrent runs. Refactors async execution to use Isolate.run for better port lifecycle management and adds debug logging for run tracking.
1 parent 3f6b044 commit 9327e4d

File tree

1 file changed

+38
-14
lines changed

1 file changed

+38
-14
lines changed

src/serious_python_android/lib/src/cpython.dart

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ export 'gen.dart';
1212

1313
CPython? _cpython;
1414
String? _logcatForwardingError;
15+
Future<void> _pythonRunQueue = Future<void>.value();
16+
var _pythonRunSeq = 0;
17+
18+
Future<T> _enqueuePythonRun<T>(Future<T> Function() action) {
19+
final completer = Completer<T>();
20+
_pythonRunQueue = _pythonRunQueue.then((_) async {
21+
try {
22+
completer.complete(await action());
23+
} catch (e, st) {
24+
completer.completeError(e, st);
25+
}
26+
});
27+
return completer.future;
28+
}
29+
1530
const _logcatInitScript = r'''
1631
import sys
1732
@@ -45,21 +60,30 @@ CPython getCPython(String dynamicLibPath) {
4560

4661
Future<String> runPythonProgramFFI(bool sync, String dynamicLibPath,
4762
String pythonProgramPath, String script) async {
48-
if (sync) {
49-
// Sync run: do not involve ports (avoids GC/close races).
50-
return _runPythonProgram(dynamicLibPath, pythonProgramPath, script);
51-
} else {
52-
// Async run: execute in a separate isolate and await exactly one result.
53-
final receivePort = ReceivePort();
54-
try {
55-
await Isolate.spawn(runPythonProgramInIsolate,
56-
[receivePort.sendPort, dynamicLibPath, pythonProgramPath, script]);
57-
final message = await receivePort.first;
58-
return message is String ? message : message.toString();
59-
} finally {
60-
receivePort.close();
63+
return _enqueuePythonRun(() async {
64+
final runId = ++_pythonRunSeq;
65+
spDebug(
66+
"Python run#$runId start (sync=$sync, script=${script.isNotEmpty}, program=$pythonProgramPath)");
67+
if (sync) {
68+
// Sync run: do not involve ports (avoids GC/close races).
69+
final result = _runPythonProgram(dynamicLibPath, pythonProgramPath, script);
70+
spDebug("Python run#$runId done (resultLength=${result.length})");
71+
return result;
72+
} else {
73+
// Async run: use Isolate.run() to avoid manual port lifecycle issues.
74+
try {
75+
final result = await Isolate.run(() =>
76+
_runPythonProgram(dynamicLibPath, pythonProgramPath, script));
77+
spDebug("Python run#$runId done (resultLength=${result.length})");
78+
return result;
79+
} catch (e, st) {
80+
final message = "Dart error running Python: $e\n$st";
81+
spDebug(message);
82+
spDebug("Python run#$runId failed");
83+
return message;
84+
}
6185
}
62-
}
86+
});
6387
}
6488

6589
Future<String> runPythonProgramInIsolate(List<Object> arguments) async {

0 commit comments

Comments
 (0)