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

[draft][browser][MT] ConfigureAwait(false) ignored when JSSynchronizationContext is installed #87326

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public class Task : IAsyncResult, IDisposable
private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags
private TaskStateFlags StateFlags => (TaskStateFlags)(m_stateFlags & ~(int)TaskStateFlags.OptionsMask); // Private property used to help with debugging

#if FEATURE_WASM_THREADS
private static Type? s_jsSynchronizationContextType;
#endif

[Flags]
internal enum TaskStateFlags
{
Expand Down Expand Up @@ -2515,6 +2519,14 @@ internal void SetContinuationForAwait(
goto HaveTaskContinuation;
}
}
#if FEATURE_WASM_THREADS
// when JSSynchronizationContext is installed, we use it regardless of continueOnCapturedContext
else if (SynchronizationContext.Current is SynchronizationContext syncCtx && syncCtx.GetType() == s_jsSynchronizationContextType)
{
tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext);
goto HaveTaskContinuation;
}
#endif

if (flowExecutionContext)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public static void InstallSynchronizationContext (JSMarshalerArgument* arguments
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
try
{
JSHostImplementation.InstallWebWorkerInterop(true);
JSHostImplementation.InstallWebWorkerInterop(true, true);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,12 @@ public static JSObject CreateCSOwnedProxy(nint jsHandle)
}

#if FEATURE_WASM_THREADS
public static void InstallWebWorkerInterop(bool installJSSynchronizationContext)
public static void InstallWebWorkerInterop(bool installJSSynchronizationContext, bool isMainThread)
{
if (isMainThread)
{
SetJSSynchronizationContextType();
}
Interop.Runtime.InstallWebWorkerInterop(installJSSynchronizationContext);
if (installJSSynchronizationContext)
{
Expand Down Expand Up @@ -236,6 +240,7 @@ public static void UninstallWebWorkerInterop()

private static FieldInfo? thread_id_Field;
private static FieldInfo? external_eventloop_Field;
private static FieldInfo? task_s_jsSynchronizationContextType_Field;

// FIXME: after https://github.com/dotnet/runtime/issues/86040 replace with
// [UnsafeAccessor(UnsafeAccessorKind.Field, Name="external_eventloop")]
Expand All @@ -261,6 +266,16 @@ public static IntPtr GetNativeThreadId()
return (int)(long)thread_id_Field.GetValue(Thread.CurrentThread)!;
}

[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicFields, "System.Threading.Tasks.Task", "System.Private.CoreLib")]
public static void SetJSSynchronizationContextType()
{
if (task_s_jsSynchronizationContextType_Field == null)
{
task_s_jsSynchronizationContextType_Field = typeof(Task).GetField("s_jsSynchronizationContextType", BindingFlags.NonPublic | BindingFlags.Static)!;
}
task_s_jsSynchronizationContextType_Field.SetValue(null, typeof(JSSynchronizationContext));
}

#endif

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static Task<T> RunAsync<T>(Func<Task<T>> body, CancellationToken cancella
return;
}

JSHostImplementation.InstallWebWorkerInterop(true);
JSHostImplementation.InstallWebWorkerInterop(true, false);
var childScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task<T> res = body();
// This code is exiting thread main() before all promises are resolved.
Expand Down Expand Up @@ -68,7 +68,7 @@ public static Task RunAsyncVoid(Func<Task> body, CancellationToken cancellationT
return;
}

JSHostImplementation.InstallWebWorkerInterop(true);
JSHostImplementation.InstallWebWorkerInterop(true, false);
var childScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task res = body();
// This code is exiting thread main() before all promises are resolved.
Expand Down Expand Up @@ -105,7 +105,7 @@ public static Task Run(Action body, CancellationToken cancellationToken)
return;
}

JSHostImplementation.InstallWebWorkerInterop(false);
JSHostImplementation.InstallWebWorkerInterop(false, false);
try
{
body();
Expand Down
2 changes: 1 addition & 1 deletion src/mono/sample/wasm/browser-threads-minimal/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public static async Task<string> FetchBackground(string url)
Console.WriteLine($"smoke: FetchBackground 2 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}");
var x=JSHost.ImportAsync(fetchhelper, "./fetchhelper.js");
Console.WriteLine($"smoke: FetchBackground 3A ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}");
using var import = await x;
using var import = await x.ConfigureAwait(false);
Console.WriteLine($"smoke: FetchBackground 3B ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}");
var r = await GlobalThisFetch(url);
Console.WriteLine($"smoke: FetchBackground 4 ManagedThreadId:{Thread.CurrentThread.ManagedThreadId}, SynchronizationContext: {SynchronizationContext.Current?.GetType().FullName ?? "null"}");
Expand Down