fix(coverage): document RPC error codes 105/106 and memoize test hitmap process - #2452
fix(coverage): document RPC error codes 105/106 and memoize test hitmap process#2452kevmoo wants to merge 1 commit into
Conversation
Package publishingIf you have publishing permissions, you can use the links below to publish the changes after merging this PR.
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. |
There was a problem hiding this comment.
Code Review
This pull request introduces error handling when resuming an isolate in IsolatePausedListener by catching and ignoring expected SentinelException and specific RPCError codes (105 and 106). Additionally, it optimizes lcov_test.dart by caching the hit map future to prevent redundant recreation. There are no review comments, so no feedback is provided.
|
|
||
| Future<Map<String, HitMap>> _getHitMap() => _hitMapFuture ??= _createHitMap(); | ||
|
|
||
| Future<Map<String, HitMap>> _createHitMap() async { |
There was a problem hiding this comment.
Speeds up this test by many minutes!!
PR HealthUnused Dependencies ✔️
For details on how to fix these, see dependency_validator. This check can be disabled by tagging the PR with Breaking changes ✔️
This check can be disabled by tagging the PR with API leaks ✔️The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
This check can be disabled by tagging the PR with Coverage ✔️
This check for test coverage is informational (issues shown here will not fail the PR). This check can be disabled by tagging the PR with License Headers ✔️
All source files should start with a license header. This check can be disabled by tagging the PR with |
| // - 106: IsolateMustBePaused / CannotResume (isolate cannot be resumed) | ||
| const isolateExited = 105; | ||
| const isolateMustBePaused = 106; | ||
| if (e.code != isolateExited && e.code != isolateMustBePaused) { |
There was a problem hiding this comment.
It's been a while since I've touched this code, but IIRC, I didn't want to ignore these exceptions because it usually means there's some sort of logical error in the (very complicated) isolate lifecycle management flow. Ignoring them turns them into harder to diagnose issues such as test processes just never terminating, or missing coverage data. I've used these exceptions several times to tighten up the lifecycle management.
Are you seeing this error in practice?
There was a problem hiding this comment.
Yes, we see this error in practice during rapid isolate teardowns, especially when running high-concurrency coverage suites across platforms (like the incoming Web/Chrome coverage support where isolate lifecycles turn over quickly).
Here is the exact race condition:
IsolatePausedListener listens to VM service events asynchronously (_onStart, _onPause, _onExit) and backfills existing isolates from the event stream. During fast teardowns, an isolate frequently completes its work and exits right in the window after _onIsolatePaused(isolateRef, ...) finishes awaiting, but just before _service.resume(isolateRef.id!) is called.
When _service.resume executes on an isolate that just exited milliseconds prior, the VM service throws RPCError code 105 (IsolateExited) or 106 (IsolateMustBePaused / CannotResume), or SentinelException (isolate collected). If uncaught, these benign teardown race conditions crash the listener loop or cause unhandled exceptions during test_with_coverage teardown.
To make sure we don't mask real logical errors in lifecycle management, we explicitly filter for those specific error codes:
if (e.code != isolateExited && e.code != isolateMustBePaused) {
rethrow;
}There was a problem hiding this comment.
Ok. Can you move the try/catch directly to the failing _service.resume call then?
There was a problem hiding this comment.
Also, is this failure mode reproducible in a test?
Summary
IsolateExited) and 106 (IsolateMustBePaused) inisolate_paused_listener.dart._getHitMap()inlcov_test.dartto avoid spawning 17+ separate Dart processes during test runs, reducing test execution time from 2.5 minutes to 8 seconds.