replace forkpty() with posix_openpt() + posix_spawn() (T-96)
`forkpty` calls `fork()` underneath. `fork()` in a multithreaded process is unsafe: only the calling thread survives in the child, but libc locks held by other threads remain "locked forever." With the multi-threaded Dart VM as parent, ~5% of spawns deadlocked in the child before `execve` (forensic probe: child stuck in S state with comm=`DartWorker`, master fd never sees POLLIN). `posix_spawn` uses `vfork` on glibc/musl/macOS, keeping the parent suspended until execve completes — no Dart code runs in the child. Pty pair built via the POSIX-standard `posix_openpt` / `grantpt` / `unlockpt` / `ptsname` sequence. Probed: zero hangs in 300 sequential spawns vs ~5% before. Behavior change: missing executable / missing workingDirectory now surface as a `PtyException` thrown by `NativePty.start` rather than a diagnostic written from the child to the slave PTY. Cleaner error path for callers. Side benefit: drops the `libutil.so.1` dynamic-library dependency. PTY now resolves entirely against libc via `DynamicLibrary.process()`. Splits the library-level `@Tags(['forkpty'])` on session_test.dart into a per-test tag, so the now-runnable-under-flutter-test cases contribute to coverage. `dart_test.yaml` declares the tag so the exclude-tags filters honor it. Drops the `retry: 2` workaround from the formerly-flaky registry test. D-5 amended. Trims session-introduced CHANGELOG entries that were over-verbose for the Keep-a-Changelog format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-30
@@ -46,26 +46,10 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
||||
end target is 95% (D-66). `ci/test.sh` now writes
|
||||
`coverage/lcov.info` as a side effect of the unit/widget/golden
|
||||
run so the gate adds no extra test invocation.
|
||||
- Test sweep covering `kernel/src/commands/keybindings.dart` (KeyEvent
|
||||
modifier mapping, parse-error edges, resolver entries view),
|
||||
`kernel/src/toolchain_paths.dart` (the Flutter-free `ToolchainView.resolved`
|
||||
static view), `widgets/src/clide_tooltip.dart` (hover-delay overlay,
|
||||
flip-above placement, re-entry cycle), `widgets/src/clide_palette.dart`
|
||||
(filter typing, submit-invokes-first, hover state),
|
||||
`widgets/src/multitab_controller.dart` (`copyWith`, `length`/`isEmpty`
|
||||
getters), and `widgets/src/clide_markdown.dart` (h3–h6, tables,
|
||||
strikethrough, default block fallback, record-link tap). Crosses
|
||||
the 93% line-coverage threshold (T-91).
|
||||
- `kernel/src/syntax/tree_sitter_service.dart` test sweep — every
|
||||
`colorForRole` switch arm plus fake-FFI coverage of
|
||||
`_init`/`_loadGrammar`/`highlight`/`dispose` branches, taking the
|
||||
file from 17% → 96%. Real-library smoke test
|
||||
(`test/kernel/src/syntax/tree_sitter_smoke_test.dart`) dlopen's the
|
||||
vendored `native/linux-x64/libtree-sitter.so`, loads the bundled
|
||||
`dart` grammar end-to-end, and verifies highlight emits sane spans —
|
||||
catches FFI signature drift the fake-driven tests can't. Skips on
|
||||
non-Linux and when the vendored library isn't present. Drives total
|
||||
line coverage across the 95% target (T-91).
|
||||
- Test sweep — `keybindings`, `toolchain_paths`, and several
|
||||
`widgets/src/` primitives (tooltip, palette, multitab, markdown).
|
||||
- `tree_sitter_service` sweep — fake-FFI + real-library smoke,
|
||||
17% → 96%. Crosses the 95% global target (T-91).
|
||||
- Staged `dart doc` CI job — generates and uploads an HTML API
|
||||
reference for the public `lib/` surface. The step wraps
|
||||
`dart doc --validate-links` and grep-fails the build on any warning,
|
||||
@@ -119,16 +103,14 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
||||
|
||||
### Changed
|
||||
|
||||
- Pre-push line-coverage floor ratcheted to 95% — D-66 target hit.
|
||||
- `TreeSitterService` accepts injectable `TreeSitterLib` and grammar /
|
||||
query loaders so tests can substitute a fake FFI surface without
|
||||
dlopen'ing `libtree-sitter.so`. `TreeSitterLib.testing(...)` exposes a
|
||||
named-parameter constructor with safe no-op defaults for every native
|
||||
function; `TreeSitterLib.fromDynamicLibrary(...)` lets the smoke test
|
||||
load the vendored `.so` directly. Production paths
|
||||
(`TreeSitterService.shared`, `TreeSitterLib.instance`) unchanged.
|
||||
- Tidied test imports — dropped redundant `dart:ui` / `dart:typed_data`
|
||||
/ barrel-redundant package imports flagged by `unnecessary_import`.
|
||||
- PTY spawning uses `posix_openpt` + `posix_spawn` instead of
|
||||
`forkpty` — closes a ~5% deadlock window in the multithreaded Dart
|
||||
VM (T-96, D-5 amended). Missing exe/cwd now throw `PtyException` at
|
||||
spawn time. Drops the `libutil.so.1` dependency.
|
||||
- Coverage floor ratcheted to 95% — D-66 target hit.
|
||||
- `TreeSitterService` and `TreeSitterLib` accept injectable FFI + asset
|
||||
loaders for fake-driven tests; production paths unchanged.
|
||||
- Tidied test imports flagged by `unnecessary_import`.
|
||||
- Terminal panes now render bold attributes with a real bold weight —
|
||||
bundled JetBrainsMono Bold + BoldItalic are registered with the
|
||||
`JetBrainsMono` family at `weight: 700`. The painter's bold
|
||||
|
||||
Reference in New Issue
Block a user