-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[coverage] Proactively collect isolates as they pause #595
Conversation
PR HealthBreaking changes ✔️
Changelog Entry ✔️
Changes to files need to be accounted for in their respective changelogs. Coverage ✔️
This check for test coverage is informational (issues shown here will not fail the PR). 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.
License Headers ✔️
All source files should start with a license header. Package publish validation ✔️
Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM so far! Let me know when you're ready for a final review.
/// - Each callback will only be called once per isolate. | ||
Future<void> listenToIsolateLifecycleEvents( | ||
VmService service, | ||
void Function(IsolateRef isolate) onIsolateStarted, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: consider creating a typedef for all these callback types. Maybe something like: typedef FutureOr<void> Function(IsolateRef) IsolateLifecycleCallback
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've made some typedefs, but it's important to distinguish the async callbacks from the sync ones. If all of them were allowed to be async, then ensuring they don't interleave would be more complicated.
Ready for review |
Verified this works on a real repo: dart-lang/native#1629 |
This is great! Thanks @liamappelbe! 🚀 |
Yay! Thanks @liamappelbe 🎉 |
Once I publish this, it will be auto-rolled into flutter. So I'll hold off publishing for a few days to let Flutter 3.27 stabilize. This fix doesn't affect Flutter anyway. |
The old coverage collection flow was to wait for all isolates to reach a paused state, then collect coverage for all the isolate groups, then resume all the isolates. This caused a bug where isolate A is waiting for isolate B to exit, but isolate B is paused waiting to be resumed by package:coverage, so isolate A never enters the paused state, and the test deadlocks.
The basic idea of the new flow is to collect coverage for isolates as they pause, and immediately resume them, and keep doing this until all isolates have exited. This fixes the bug, but there are a whole lot of complications (see the details section).
The new flow is more streamlined, resulting in a modest performance boost (mainly because the
_waitIsolatesPaused
function was pretty inefficient). A small test suite that runs in 4.38 sec without coverage, used to run with coverage in 7.91 secs, but now runs in 6.38 secs.This change doesn't affect Flutter, because Flutter sets
waitPaused
to false. They have their own isolate management logic.Details
IsolateEventBuffer
class.listenToIsolateLifecycleEvents
cleans up the event stream.IsolateGroupState
tracks the running/paused isolates in a groupIsolatePausedListener
ties all that infra togetheronIsolatePaused
callback with a flag indicating whether that's the last isolate in the group._waitIsolatesPaused
has been removed and instead we useIsolatePausedListener
to collect coverage for the last isolate in each group.Fixes #520