DartBridge.hardExit, and re-pin python-build to 20260908 - #248
Merged
Conversation
dart:io's exit() runs the normal C teardown, which destroys the C++ statics inside every loaded CPython extension module. The interpreter lives on a detached thread that may still be running, so that teardown can fault it, giving a SIGSEGV on exit from an app that had already finished its work. The reported case died in matplotlib's ft2font looking up a pybind11 type-caster map that had just been destructed. hardExit routes to serious_python_hard_exit in dart_bridge 1.9.0, which terminates without running atexit handlers or static destructors. The symbol is resolved through the existing _lookupOrNull soft lookup, so an app built against an older libdart_bridge still loads; canHardExit is then false and the caller falls back to exit(). Version tables regenerated from python-build release 20260908, which carries dart_bridge 1.9.0. No CPython or Pyodide versions change.
… versions Every serious_python_* package gets a 4.7.0 entry, matching the convention that each release is recorded in each package's own changelog. The platform-interface entry carries the substance, since DartBridge.hardExit lives there; the platform packages record the python-build re-pin and note that no CPython or Pyodide versions move. The darwin podspec and the android build.gradle.kts carry their own version declarations alongside the pubspec, and both were still on 4.6.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
dart:io'sexit()runs the normal C teardown, which destroys the C++ statics inside every loaded CPython extension module. The interpreter lives on a detached thread (sp_worker) that may still be running, so that teardown can fault it: aSIGSEGVon exit from an app that had already finished its work.The reported case died in matplotlib's
ft2fontlooking up a pybind11 type-caster map that had just been destructed:Nothing is matplotlib-specific. A second capture of the same crash showed the main thread inside
SkTypefaceCache::~SkTypefaceCache()(Flutter's own Skia static) on the same__cxa_finalize_rangespass.Change
DartBridge.hardExit(exitCode)/DartBridge.canHardExitindart_bridge_ffi.dart, bindingserious_python_hard_exitfrom dart_bridge 1.9.0 (flet-dev/dart-bridge#20). It terminates without runningatexithandlers or C++ static destructors, and does not return.The symbol is resolved through the existing
_lookupOrNullsoft lookup, the same mechanism already used forisPythonInitializedandsignalDartSession, so an app built against an olderlibdart_bridgestill loads.canHardExitis thenfalse,hardExitis a no-op, and the caller falls back toexit()with the pre-existing behaviour.Version tables regenerated with
dart run serious_python:gen_version_tables --release-date 20260908, movingdart_bridge_versionto 1.9.0 andpython_build_release_dateto 20260908 across the fourpython_versions.propertiesfiles andpython_versions.dart. No CPython or Pyodide versions change; the python-build release (flet-dev/python-build#41) exists to publish the updated manifest.Versions and changelogs. All six packages go to 4.7.0, each with its own
CHANGELOG.mdentry: the substance sits inserious_python_platform_interface(wherehardExitlives) andserious_python, with the platform packages recording the python-build re-pin and noting that no CPython or Pyodide versions move.serious_python_darwin's podspec andserious_python_android'sbuild.gradle.ktscarry their own version declarations alongside the pubspec and are bumped too.Verification
Built a Flet app bundling matplotlib/numpy/Pillow against the published 20260908 and dart_bridge 1.9.0 artifacts, with the local cache cleared first so the real signed xcframework was downloaded:
sys.exit(3), afterSharedPreferencesdurability, both exit pathslibdart_bridge(1.8.0) with this Dart sidecanHardExitfalse, falls backAt the observed baseline rate, twelve clean closes by chance is roughly 0.8%.
Caveat on the fallback: the soft lookup covers an older native
libdart_bridgeat runtime, which is verified above. It does not cover an olderserious_pythonDart package, whereDartBridge.hardExitdoes not exist and a consumer pinning the template to 4.7.0 would fail to compile. That is inherent, but worth stating since it is narrower than "backwards compatible" suggests.Not verified: the Windows and Linux paths. dart_bridge's
TerminateProcessbranch in particular is reasoned fromDLL_PROCESS_DETACHrunning static destructors, not tested on a Windows host.Chain
serious_pythonpin to 4.7.0 and useshardExitin the build template