Skip to content

Document and fix dispatchYield codepath #4255

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

Merged
merged 2 commits into from
Oct 21, 2024
Merged

Conversation

qwwdfsad
Copy link
Collaborator

No description provided.

* Always unpark a worker to avoid potential starvation in cases when coroutine was launched via UNDISPATCHED mechanism

Fixes #4248
@qwwdfsad qwwdfsad changed the title Document dispatchYield Document and fix dispatchYield codepath Oct 21, 2024
@qwwdfsad qwwdfsad requested a review from dkhalanskyjb October 21, 2024 10:14
@qwwdfsad
Copy link
Collaborator Author

I skipped the test, but I checked the original report, and it is resolved.

I feel uneasy writing a very dedicated yield-targeting stress test for the codepath that no longer exist

* task sequence [#1, #2, #3], the scheduling of task #4 will produce
* [#4, #1, #2, #3], allocates new worker and makes #4 stealable after some time.
* On a fast enough system, it means that `while (true) { yield() }` might obstruct the progress
* of the system and potentially starve it.
Copy link
Collaborator

@dkhalanskyjb dkhalanskyjb Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you're proposing is better than what we have now, but it's still not ideal. We are just one step farther from seeing the same effect. LoggingDispatcher(Dispatchers.IO) will exhibit this behavior, as the author of LoggingDispatcher can't override dispatchYield. That's a problem we're also facing with Delay (people can't wrap Delay implementations and expect the same behavior).

I see the following options of dealing with this problem for good:

  • Make the YieldContext marker used by Dispatchers.Unconfined available to all dispatchers (and not just those not needing a dispatch) and check its presence when dispatch'ing to the coroutine scheduler; remove dispatchYield completely. Probably too slow to check that on every dispatch?
  • Apply the proposed fix, but also design and publish some support for wrapping coroutine dispatchers that takes care to propagate dispatchYield.
  • Make dispatchYield a valid optimization in a wide range of scenarios (including things like "skip suspensions on Dispatchers.Main if the looper is empty anyway") by recognizing the coroutine not being yet dispatched to the correct execution context as a separate state. EDIT: ... and make dispatchYield public, as it will no longer be deeply tied to our implementation details.

Thoughts?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, and it seems like the proper solution includes a solution for Delay as well.
In general, it's not really sustainable to include @Internal* methods or superinterfaces into the class that we expect people to extend.

Probably too slow to check that on every dispatch?

Yes, this option we'd rather avoid -- quite a non-deterministic check for the sake of code path that is really rare.

I would merge this as is and, as a next step, would consider sticking to the third option.
Probably, making dispatchYield public should be enough, not sure about the yield optimization (as it's quite niche)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about the yield optimization (as it's quite niche)

My idea actually wasn't to introduce the optimizations (they are even more niche than even yield(), which itself is niche) (though introducing the optimizations actually shouldn't be difficult) but to ensure that if someone does override dispatchYield in favor of a non-trivial optimized implementation, that implementation wouldn't break the way ours did in #4248. If we introduce dispatchYield for public implementation without fixing the state machine, the most obvious usage of dispatchYield becomes a footgun.

@qwwdfsad qwwdfsad merged commit d30af7c into develop Oct 21, 2024
1 check passed
@qwwdfsad qwwdfsad deleted the dispatch-yield-doc branch October 21, 2024 15:43
knisht added a commit to JetBrains/intellij-deps-kotlinx.coroutines that referenced this pull request Mar 31, 2025
This reverted introduces regression IJPL-182130. We did not get this deadlock before, so it seems safe to revert the commit

This reverts commit d30af7c.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants