@@ -14,6 +14,7 @@ CPython? _cpython;
1414// Keep a single interpreter per process; repeated init/finalize of CPython 3.12
1515// from an embedder is fragile and was crashing on second launch.
1616bool _pythonInitialized = false ;
17+ Pointer <PyThreadState >? _mainThreadState;
1718
1819CPython getCPython (String dynamicLibPath) {
1920 return _cpython ?? = _cpython = CPython (DynamicLibrary .open (dynamicLibPath));
@@ -56,6 +57,8 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
5657 final cpython = getCPython (dynamicLibPath);
5758 if (! _pythonInitialized) {
5859 cpython.Py_Initialize ();
60+ // Release the GIL from the init thread so other threads can reacquire it.
61+ _mainThreadState = cpython.PyEval_SaveThread ();
5962 _pythonInitialized = true ;
6063 debugPrint ("after Py_Initialize()" );
6164 } else {
@@ -64,23 +67,37 @@ Future<String> runPythonProgramInIsolate(List<Object> arguments) async {
6467
6568 var result = "" ;
6669
67- if (script != "" ) {
68- // run script
69- final scriptPtr = script.toNativeUtf8 ();
70- int sr = cpython.PyRun_SimpleString (scriptPtr.cast <Char >());
71- debugPrint ("PyRun_SimpleString for script result: $sr " );
72- malloc.free (scriptPtr);
73- if (sr != 0 ) {
74- result = getPythonError (cpython);
75- }
76- } else {
77- // run program
78- final moduleNamePtr = programModuleName.toNativeUtf8 ();
79- var modulePtr = cpython.PyImport_ImportModule (moduleNamePtr.cast <Char >());
80- if (modulePtr == nullptr) {
81- result = getPythonError (cpython);
70+ // Reacquire the GIL using the saved main thread state for this process.
71+ final mainState = _mainThreadState;
72+ if (mainState == null ) {
73+ throw StateError ("Python main thread state is null after initialization" );
74+ }
75+
76+ cpython.PyEval_RestoreThread (mainState);
77+
78+ try {
79+ if (script != "" ) {
80+ // run script
81+ final scriptPtr = script.toNativeUtf8 ();
82+ int sr = cpython.PyRun_SimpleString (scriptPtr.cast <Char >());
83+ debugPrint ("PyRun_SimpleString for script result: $sr " );
84+ malloc.free (scriptPtr);
85+ if (sr != 0 ) {
86+ result = getPythonError (cpython);
87+ }
88+ } else {
89+ // run program
90+ final moduleNamePtr = programModuleName.toNativeUtf8 ();
91+ var modulePtr =
92+ cpython.PyImport_ImportModule (moduleNamePtr.cast <Char >());
93+ if (modulePtr == nullptr) {
94+ result = getPythonError (cpython);
95+ }
96+ malloc.free (moduleNamePtr);
8297 }
83- malloc.free (moduleNamePtr);
98+ } finally {
99+ // Drop the GIL again so subsequent runs/threads can re-acquire it cleanly.
100+ _mainThreadState = cpython.PyEval_SaveThread ();
84101 }
85102
86103 // cpython.Py_Finalize();
0 commit comments