Skip to content
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

Remove the use of context within IAsyncOperation to fire handlers. #1131

Merged
merged 2 commits into from
Mar 10, 2022

Conversation

manodasanW
Copy link
Member

Per the discussion in #1126, the async operation itself shouldn't synchronize the context which is used to fire handlers. Instead, it is the responsibility of the awaiter to capture the context and synchronize if the caller of the async operation would like to. For instance, the awaiter in C# tasks by default captures context and fires the handler on the same context by making use of SynchronizationContext, but the caller is also able to configure that behavior using ConfigureAwait. Similar functionality also exists in C++/WinRT via co_await. Due to that, I am initially making a change to stop using the context to fire handlers which should also resolve the deadlock in the below mentioned issue. In a future release, we can then clean up the code assuming we hear about no issues from this change. Testing of some apps with this change have so far shown no issues.

Fixes #670

@manodasanW manodasanW merged commit d015a5f into master Mar 10, 2022
@manodasanW manodasanW deleted the manodasanw/syncronizationcontext3 branch March 10, 2022 03:29
@mqudsi
Copy link

mqudsi commented Mar 10, 2022

Is there an easy way to test this in a WinAppSdk project without waiting for a new release? (Perhaps by adding a reference to a nightly CsWinRt build?) I have some projects that hang when providing streams as web resources, regardless of the thread apartment model.

@manodasanW
Copy link
Member Author

@mqudsi The fix is in the Windows SDK projection generated using CsWinRT. We are planning on having a CsWinRT release along with a Windows SDK projection release by early next week after which you would be able to use the WindowsSdkPackageVersion property in your project to override the version of it in the .NET SDK and use that version. Would that work for you?

Otherwise you would need to try to generate the Windows SDK projection using the CsWinRT version built using what is in master.

One thing I discovered when investigating this issue is that part of the problem was that WebView was calling CreateStreamOverRandomAccessStream on the UI thread to convert the IRandomStream passed as a content to an IStream. This was what was the core of the deadlock and not the thread which the IRandomStream is created on.

@mqudsi
Copy link

mqudsi commented Mar 10, 2022

@manodasanW that should definitely be sufficient, as I'm only using CsWinRt via the WindowsAppSdk nuget package for the projects in question. (I remember the earlier WinUI 3 releases had us add a separate nuget reference to CsWinRt, but I can see now that CsWinRt seems to be baked into the WindowsAppSdk binary as its not even a transitive dependency.)

Thanks for explaining your discovery about the reasons for the deadlock. I'm guessing the reason WebView2 didn't block the UI thread if given a native WinRT stream rather than a Win32/.NET stream turned into an IRandomAccessStream (created from the same thread context/under the same scheduler) has to do with the fact that a concrete WinRT FileInputStream/XxxStream is marshalled as an agile object directly while the Win32 stream (for some reason) isn't? (Although both end up converted to the non-agile IRandomAccessStream, I imagine some marshaling operation is optimized to a no-op dependent on the underlying concrete object?)

My reference to the apartment model was because of the old version of the docs which stated

The WebView2 must be created on a UI thread. Specifically a thread with a message pump. All callbacks occur on that thread and requests into the WebView2 must be done on that thread. It is not safe to use the WebView2 from another thread.

The only exception is for the Content property of CoreWebView2WebResourceRequest. The Content property stream is read from a background thread. The stream should be agile or be created from a background STA to prevent performance impact to the UI thread.

That sent me on a wild goose chase of trying to create the Win32/.NET stream on a background STA thread (though I could not see how that could actually be the case given the observed behavior).

@manodasanW
Copy link
Member Author

@mqudsi The Windows SDK projection is actually shipped as a targeting pack which is implicitly pulled in by the .NET SDK when you use the net<5/6/7>.0-windows10.0.xxx.0 TFMs. WindowsAppSDK takes an implicit dependency on it via the TFM.

Yesterday, we shipped an updated version of the Windows SDK projection and that will be pulled in by the next .NET SDK update when it comes out. But in the meantime, if you want to try out the fix, you can make .NET SDK use a version of the Windows SDK projection you specify by setting the following property in your application project:

<WindowsSdkPackageVersion>10.0.<windows_api_version_from_tfm>.24</WindowsSdkPackageVersion>

You should remove this once the next .NET SDK update comes out and you move to it given it overrides the Windows SDK projection version even if the .NET SDK has a newer version available.

With regards to the other point, I believe internally in the OS there are optimizations for the WinRT InMemoryRandomAccessStream which meant that the non agile IStream was taking advantage of the agile behavior of it whereas for other passed IRandomMemoryStreams such as the one we implement, it doesn't.

@mqudsi
Copy link

mqudsi commented Mar 18, 2022

I hate that Visual Studio lets me (or even prompts me) to upgrade to a newer version of a dependency that doesn't support the runtime - using WinAppSDK 1.0.0 or 1.0.1 completely breaks ResolvePackageDependencies for the WAP/package project w/ obscure error messages that don't really hint at what's going on - but none of that is your problem!

The good news: as you suggested, just pinning WindowsSdkPackageVersion to 10.0.x.24 did the trick and took care of all the synchronization issues I was experiencing mixing-and-matching Win32 and WinRT calls in the WebView2 callback. Thank you very much @manodasanW both for fixing the issue and for your insight/guidance on what's going on!

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.

WebView2 hangs on reading final 0 bytes via NetFxToWinRtStreamAdapter
4 participants