-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Avoid inlining ILanguageClientBroker initialization #65929
Conversation
Fixes: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1698422 StartListening is called on thread pool, which causes LanguageClientBroker to be initialized inline without yielding and gets indirectly blocked on by the UI thread. Instead, yield so that StartListening continues without blocking on the initialization.
await TaskScheduler.Default; | ||
// doesn't block the UI thread. Note, we always yield because sometimes our caller starts | ||
// on the threadpool thread but is indirectly blocked on by the UI thread. | ||
await TaskScheduler.Default.SwitchTo(alwaysYield: true); |
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.
@sharwell should we just make this code the pattern we always use? (and/or have a helper we ourselves use that always does this). I'd like to ensure we aren't just patching this same issue oover and over again in multiple places.
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.
I guess I'm not sure why we didn't just Task.Run() in the StartListening call here...
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.
should we just make this code the pattern we always use
No, that would be a waste. await TaskScheduler.Default
is a safe and clear way to ensure the asynchronous operation is on a background thread, and NOP if you already are.
// doesn't block the UI thread. Note, we always yield because sometimes our caller starts | ||
// on the threadpool thread but is indirectly blocked on by the UI thread. |
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.
We might want to put a comment here explaining this case a bit more since this is otherwise very not obvious without looking at this bug.
await TaskScheduler.Default; | ||
// doesn't block the UI thread. Note, we always yield because sometimes our caller starts | ||
// on the threadpool thread but is indirectly blocked on by the UI thread. | ||
await TaskScheduler.Default.SwitchTo(alwaysYield: true); |
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.
I guess I'm not sure why we didn't just Task.Run() in the StartListening call here...
// doesn't block the UI thread. Note, we always yield because sometimes our caller starts | ||
// on the threadpool thread but is indirectly blocked on by the UI thread. |
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.
The fact that the caller is indirectly blocking the UI thread is somewhat orthogonal to the goal here. We always yield because this method is designed to always be non-blocking, regardless of the calling scenario.
my general issue is that there doesn't seem to be a way to sufficiently audit these. we're effectively beholden to dave noticing something and reporting an issue. |
Fixes: AB#1698422
StartListening is called on thread pool, which causes LanguageClientBroker to be initialized inline without yielding and gets indirectly blocked on by the UI thread via a JTF.Run. Instead, yield so that StartListening continues without blocking on the initialization.
Example call stack (note this is two threads stitched together using the background stacks feature):