The following test is failing against our early .NET 5 preview support:
https://github.com/xamarin/xamarin-android/blob/3f743678d3e7c19fc6f6e8f6928bfe4ae7d55ff0/src/Mono.Android/Test/Android.App/ApplicationTest.cs#L24
Download the repro here:
BuildXASdkProjectFalse.zip
Run the sample and click the button
dotnet build /t:Run ~/Downloads/BuildXASdkProjectFalse/UnnamedProject.csproj /v:n
The following is null, and the test fails
var c = System.Threading.SynchronizationContext.Current;
@pjcollins should this issue be in dotnet/runtime, or do we have to do something to fix this?
@jonathanpeppers I think we may want further triage/investigation from our runtime experts first. I didn't dig too deep into this and am not yet sure who should own it.
The reason/problem is:
In mono, there is a "cross-assembly" call to AndroidEvironment.GetDefaultSyncContext() within Mono.Android.dll if no sync context has been set.
This cross-assembly call is presumably not present in dotnet/runtime.
I suppose a workaround would be to call SynchronizationContext.SetSynchronizationContext(SynchronizationContext) as part of process startup. This would allow the main/UI thread to properly have a synchronization context.
~What I'm less certain of is what the current behavior is for non-UI threads. Do they currently get null? If not, how would we support them?~ [AndroidEnvironment.GetDefaultSyncContext() only returns non-null on the main/UI thread, and null for any other thread.]
Relatedly, I find it "interesting" that there is a "carve-out" in dotnet/runtime for UAP apps:
I find it doubly "interesting" that the UAP carve-out has a very similar "cross-assembly" call as what we currently do, using Reflection to lookup a method and then store it into a Delegate for later invocation:
This raises a long-standing -- and unanswered -- question: are Android/iOS/etc. apps all expected to use the same BCL assemblies as the rest of .NET 5, or just assemblies with the same API contract while permitting different implementations? Can we have an e.g. SynchronizationContext.Android.cs type which is in the dotnet/runtime repo?
question: are Android/iOS/etc. apps all expected to use the same BCL assemblies as the rest of .NET 5, or just assemblies with the same API contract while permitting different implementations?
The latter.
Can we have an e.g. SynchronizationContext.Android.cs type which is in the dotnet/runtime repo?
Yes (as long as it doesn't add new public API).
I suppose a workaround would be to call SynchronizationContext.SetSynchronizationContext(SynchronizationContext) as part of process startup.
@jonpryor this looks to me like good way to set it up
The general strategy is to leave it to the application model to do it, e.g. Windows Forms has code that establishes it on demand,
https://github.com/dotnet/winforms/blob/dc502e62534a3b08809dae7710fc75bee071ac8b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs#L6363-L6386, same for WPF
Relatedly, I find it "interesting" that there is a "carve-out" in dotnet/runtime for UAP apps:
That code is gone from dotnet/runtime
Looping in @stephentoub to help steer this in the right direction and avoid any reflection calls ;-)
I suppose a workaround would be to call SynchronizationContext.SetSynchronizationContext(SynchronizationContext) as part of process startup.
this looks to me like good way to set it up
Yup, that's a good direction to go.
The alternative is what app models like Windows Forms do, which is to essentially check at relevant points whether a sync ctx need to be set and do so if one is. Windows Forms does this, for example, just prior to running its message loop as well as any time a control is instantiated. I believe it does this out of concern that someone may have set a different context in the interim, overwriting its own
I find it "interesting" that there is a "carve-out" in dotnet/runtime for UAP apps
That code is gone from dotnet/runtime
Yeah, this code no longer exists, and was only there in the first place as a hack to work around some layering issues at the time. We should try not to emulate it :smile:
should we set a default SynchronizationContext in the Android test app we use to run tests in dotnet/runtime then?
Not necessarily. I assume the SynchronizationContext in question is one that marshal back to whatever UI message loop Android utilizes to drive the UI? If that's the case, then it's really about interactions with the UI. There's not going to be a sync ctx on thread pool threads, nor should there be, and if the things being tested are just as meaningful on a thread pool thread as they are on a UI thread, then there's no need to force them into that mold.
Most helpful comment
I suppose a workaround would be to call
SynchronizationContext.SetSynchronizationContext(SynchronizationContext)as part of process startup. This would allow the main/UI thread to properly have a synchronization context.~What I'm less certain of is what the current behavior is for non-UI threads. Do they currently get
null? If not, how would we support them?~ [AndroidEnvironment.GetDefaultSyncContext()only returns non-nullon the main/UI thread, andnullfor any other thread.]Relatedly, I find it "interesting" that there is a "carve-out" in dotnet/runtime for UAP apps:
https://github.com/dotnet/runtime/blob/3764f3781e6dd78e1e8533eb76e1b317e55939d8/src/coreclr/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.Uap.cs#L14-L25
I find it doubly "interesting" that the UAP carve-out has a very similar "cross-assembly" call as what we currently do, using Reflection to lookup a method and then store it into a Delegate for later invocation:
https://github.com/dotnet/runtime/blob/3764f3781e6dd78e1e8533eb76e1b317e55939d8/src/coreclr/src/System.Private.CoreLib/src/System/Threading/SynchronizationContext.Uap.cs#L49-L75
This raises a long-standing -- and unanswered -- question: are Android/iOS/etc. apps all expected to use the same BCL assemblies as the rest of .NET 5, or just assemblies with the same API contract while permitting different implementations? Can we have an e.g.
SynchronizationContext.Android.cstype which is in thedotnet/runtimerepo?