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

[browser][MT] send cancel to abandoned threads with event loop during mono_exit #97441

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7b829f9
- `cancelThreads` will exit all JSWebWorker which are still running (…
pavelsavara Jan 24, 2024
875eae2
don't load snapshot on threads
pavelsavara Jan 24, 2024
52483a4
- only register runtime into globalThis when it's ready started
pavelsavara Jan 24, 2024
3d27902
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 24, 2024
0a3a1a8
more MT smoke tests
pavelsavara Jan 24, 2024
a55dd62
improve thread prefix logging
pavelsavara Jan 25, 2024
f2354af
more
pavelsavara Jan 25, 2024
d090b37
fix
pavelsavara Jan 25, 2024
21e6f20
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 25, 2024
834b001
more
pavelsavara Jan 25, 2024
b46712f
Update src/mono/browser/runtime/pthreads/browser/index.ts
pavelsavara Jan 25, 2024
8b05ab9
feedback
pavelsavara Jan 25, 2024
7189c02
fix
pavelsavara Jan 25, 2024
3db3f68
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 25, 2024
298013d
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 25, 2024
32266cb
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 26, 2024
8e19831
ts cleanup
pavelsavara Jan 26, 2024
366732e
registration feedback
pavelsavara Jan 26, 2024
5cdf123
split test
pavelsavara Jan 26, 2024
cf0ee2f
split
pavelsavara Jan 26, 2024
82a549c
more
pavelsavara Jan 26, 2024
fed4ff3
more
pavelsavara Jan 26, 2024
63df065
thread events
pavelsavara Jan 28, 2024
8069389
more cache logging
pavelsavara Jan 28, 2024
fefe471
fix
pavelsavara Jan 28, 2024
2d434a7
Merge branch 'main' into browser_abandoned_jswebworker_timeout
pavelsavara Jan 29, 2024
a99547f
fix snapshot hash
pavelsavara Jan 29, 2024
902f173
fix
pavelsavara Jan 29, 2024
988cee6
Update src/mono/browser/runtime/interp-pgo.ts
pavelsavara Jan 29, 2024
d025490
feedback
pavelsavara Jan 29, 2024
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
21 changes: 18 additions & 3 deletions src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static async Task<int> Main(string[] args)
var includedClasses = new List<string>();
var includedMethods = new List<string>();
var backgroundExec = false;
var untilFailed = false;

for (int i = 1; i < args.Length; i++)
{
Expand Down Expand Up @@ -53,6 +54,9 @@ public static async Task<int> Main(string[] args)
case "-backgroundExec":
backgroundExec = true;
break;
case "-untilFailed":
untilFailed = true;
break;
default:
throw new ArgumentException($"Invalid argument '{option}'.");
}
Expand All @@ -72,10 +76,21 @@ public static async Task<int> Main(string[] args)
{
await Task.Yield();
}
if (backgroundExec)

var res = 0;
do
{
return await Task.Run(() => runner.Run());
if (backgroundExec)
{
res = await Task.Run(() => runner.Run());
}
else
{
res = await runner.Run();
}
}
return await runner.Run();
while(res == 0 && untilFailed);

return res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
<!-- Make debugging easier -->
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<WasmNativeStrip>false</WasmNativeStrip>
<WasmXHarnessMonoArgs>$(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true</WasmXHarnessMonoArgs>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\JSImportExportTest.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\SecondRuntimeTest.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\HttpRequestMessageTest.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\TimerTests.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\TimerTests.cs" Condition="'$(FeatureWasmThreads)' != 'true'"/>
<Compile Include="System\Runtime\InteropServices\JavaScript\Utils.cs" />
<None Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.mjs" />
<None Include="$(CompilerGeneratedFilesOutputPath)\..\browser-wasm\generated\Microsoft.Interop.JavaScript.JSImportGenerator\Microsoft.Interop.JavaScript.JSImportGenerator\JSImports.g.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Net.WebSockets;
using System.Text;
using System.Linq;
using System.Runtime.CompilerServices;

namespace System.Runtime.InteropServices.JavaScript.Tests
{
Expand Down Expand Up @@ -48,12 +49,14 @@ public async Task InitializeAsync()

#region Executors

private CancellationTokenSource CreateTestCaseTimeoutSource()
private CancellationTokenSource CreateTestCaseTimeoutSource([CallerMemberName] string memberName = "")
{
var start = DateTime.Now;
var cts = new CancellationTokenSource(TimeoutMilliseconds);
cts.Token.Register(() =>
{
Console.WriteLine($"Unexpected test case timeout at {DateTime.Now.ToString("u")} ManagedThreadId:{Environment.CurrentManagedThreadId}");
var end = DateTime.Now;
Console.WriteLine($"Unexpected test case {memberName} timeout after {end - start} ManagedThreadId:{Environment.CurrentManagedThreadId}");
});
return cts;
}
Expand Down Expand Up @@ -247,11 +250,11 @@ public async Task JSSynchronizationContext_Send_Post_To_Canceled()
Assert.False(shouldNotHitPost);
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/96628#issuecomment-1907602744")]
[Fact]
// this will say something like `JSSynchronizationContext is still installed on worker 0x4ff0030.` in the console during shutdown.
public async Task JSWebWorker_Abandon_Running()
{
var cts = new CancellationTokenSource();

TaskCompletionSource never = new TaskCompletionSource();
TaskCompletionSource ready = new TaskCompletionSource();

Expand All @@ -261,7 +264,7 @@ public async Task JSWebWorker_Abandon_Running()
{
ready.SetResult();
return never.Task;
}, cts.Token);
}, CancellationToken.None);
#pragma warning restore CS4014

await ready.Task;
Expand All @@ -273,10 +276,9 @@ public async Task JSWebWorker_Abandon_Running()
}

[Fact]
// this will say something like `JSSynchronizationContext is still installed on worker 0x4ff0030.` in the console during shutdown.
public async Task JSWebWorker_Abandon_Running_JS()
{
var cts = new CancellationTokenSource();

TaskCompletionSource ready = new TaskCompletionSource();

#pragma warning disable CS4014
Expand All @@ -287,7 +289,7 @@ public async Task JSWebWorker_Abandon_Running_JS()
var never = WebWorkerTestHelper.JSDelay(int.MaxValue);
ready.SetResult();
await never;
}, cts.Token);
}, CancellationToken.None);
#pragma warning restore CS4014

await ready.Task;
Expand All @@ -301,7 +303,7 @@ public async Task JSWebWorker_Abandon_Running_JS()
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task Executor_Propagates(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
bool hit = false;
var failedTask = executor.Execute(() =>
{
Expand All @@ -321,7 +323,7 @@ public async Task Executor_Propagates(Executor executor)
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task ManagedConsole(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(() =>
{
Console.WriteLine("C# Hello from ManagedThreadId: " + Environment.CurrentManagedThreadId);
Expand All @@ -332,7 +334,7 @@ await executor.Execute(() =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task JSConsole(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(() =>
{
WebWorkerTestHelper.Log("JS Hello from ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId);
Expand All @@ -343,7 +345,7 @@ await executor.Execute(() =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task NativeThreadId(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await executor.StickyAwait(WebWorkerTestHelper.InitializeAsync(), cts.Token);
Expand All @@ -367,7 +369,7 @@ await executor.Execute(async () =>
public async Task ThreadingTimer(Executor executor)
{
var hit = false;
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
TaskCompletionSource tcs = new TaskCompletionSource();
Expand All @@ -389,7 +391,7 @@ await executor.Execute(async () =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task JSDelay_ContinueWith(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await executor.StickyAwait(WebWorkerTestHelper.CreateDelay(), cts.Token);
Expand All @@ -405,7 +407,7 @@ await WebWorkerTestHelper.JSDelay(10).ContinueWith(_ =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task JSDelay_ConfigureAwait_True(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await executor.StickyAwait(WebWorkerTestHelper.CreateDelay(), cts.Token);
Expand All @@ -420,7 +422,7 @@ await executor.Execute(async () =>
public async Task ManagedDelay_ContinueWith(Executor executor)
{
var hit = false;
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await Task.Delay(10, cts.Token).ContinueWith(_ =>
Expand All @@ -434,7 +436,7 @@ await Task.Delay(10, cts.Token).ContinueWith(_ =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task ManagedDelay_ConfigureAwait_True(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await Task.Delay(10, cts.Token).ConfigureAwait(true);
Expand All @@ -446,7 +448,7 @@ await executor.Execute(async () =>
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task ManagedYield(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await Task.Yield();
Expand All @@ -461,44 +463,87 @@ await executor.Execute(async () =>

private async Task ActionsInDifferentThreads<T>(Executor executor1, Executor executor2, Func<Task, TaskCompletionSource<T>, Task> e1Job, Func<T, Task> e2Job, CancellationTokenSource cts)
{
TaskCompletionSource<T> readyTCS = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
TaskCompletionSource doneTCS = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
TaskCompletionSource<T> job1ReadyTCS = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
TaskCompletionSource job2DoneTCS = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var e1Done = false;
var e2Done = false;
var e1Failed = false;
Task e1;
Task e2;
T r1;

async Task ActionsInDifferentThreads1()
{
try
{
await e1Job(job2DoneTCS.Task, job1ReadyTCS);
if (!job1ReadyTCS.Task.IsCompleted)
{
job1ReadyTCS.SetResult(default);
}
await job2DoneTCS.Task;
}
catch (Exception ex)
{
Console.WriteLine("ActionsInDifferentThreads1 failed\n" + ex);
job1ReadyTCS.SetResult(default);
e1Failed = true;
throw;
}
finally
{
e1Done = true;
}
}

var e1 = executor1.Execute(async () =>
async Task ActionsInDifferentThreads2()
{
await e1Job(doneTCS.Task, readyTCS);
if (!readyTCS.Task.IsCompleted)
try
{
readyTCS.SetResult(default);
await e2Job(r1);
pavelsavara marked this conversation as resolved.
Show resolved Hide resolved
}
await doneTCS.Task;
}, cts.Token);
finally
{
e2Done = true;
}
}

var r1 = await readyTCS.Task.ConfigureAwait(true);

var e2 = executor2.Execute(async () =>
e1 = executor1.Execute(ActionsInDifferentThreads1, cts.Token);
r1 = await job1ReadyTCS.Task.ConfigureAwait(true);
if (e1Failed || e1.IsFaulted)
{
await e2Job(r1);

}, cts.Token);
await e1;
}
e2 = executor2.Execute(ActionsInDifferentThreads2, cts.Token);

try
{
await e2;
doneTCS.SetResult();
job2DoneTCS.SetResult();
await e1;
}
catch (Exception)
catch (Exception ex)
{
cts.Cancel();
job2DoneTCS.TrySetException(ex);
if (ex is OperationCanceledException oce && cts.Token.IsCancellationRequested)
{
throw;
}
Console.WriteLine("ActionsInDifferentThreads failed with: \n" + ex);
if (!e1Done || !e2Done)
{
Console.WriteLine("ActionsInDifferentThreads canceling!");
cts.Cancel();
}
throw;
}
}

[Theory, MemberData(nameof(GetTargetThreads2x))]
public async Task JSObject_CapturesAffinity(Executor executor1, Executor executor2)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();

var e1Job = async (Task e2done, TaskCompletionSource<JSObject> e1State) =>
{
Expand Down Expand Up @@ -533,7 +578,7 @@ public async Task JSObject_CapturesAffinity(Executor executor1, Executor executo
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task WebSocketClient_ContentInSameThread(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();

var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid());
var message = "hello";
Expand All @@ -556,9 +601,9 @@ await executor.Execute(async () =>


[Theory, MemberData(nameof(GetTargetThreads2x))]
public Task WebSocketClient_ResponseCloseInDifferentThread(Executor executor1, Executor executor2)
public async Task WebSocketClient_ResponseCloseInDifferentThread(Executor executor1, Executor executor2)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();

var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid());
var message = "hello";
Expand Down Expand Up @@ -587,13 +632,13 @@ public Task WebSocketClient_ResponseCloseInDifferentThread(Executor executor1, E
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "bye", CancellationToken.None);
};

return ActionsInDifferentThreads<ClientWebSocket>(executor1, executor2, e1Job, e2Job, cts);
await ActionsInDifferentThreads<ClientWebSocket>(executor1, executor2, e1Job, e2Job, cts);
}

[Theory, MemberData(nameof(GetTargetThreads2x))]
public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor executor2)
public async Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor executor2)
{
var cts = new CancellationTokenSource();
using var cts = CreateTestCaseTimeoutSource();

var uri = new Uri(WebWorkerTestHelper.LocalWsEcho + "?guid=" + Guid.NewGuid());
var message = ".delay5sec"; // this will make the loopback server slower
Expand All @@ -620,7 +665,7 @@ public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor
Assert.Equal(cts2.Token, ex.CancellationToken);
};

return ActionsInDifferentThreads<ClientWebSocket>(executor1, executor2, e1Job, e2Job, cts);
await ActionsInDifferentThreads<ClientWebSocket>(executor1, executor2, e1Job, e2Job, cts);
}

#endregion
Expand All @@ -630,7 +675,7 @@ public Task WebSocketClient_CancelInDifferentThread(Executor executor1, Executor
[Theory, MemberData(nameof(GetTargetThreads))]
public async Task HttpClient_ContentInSameThread(Executor executor)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();
var uri = WebWorkerTestHelper.GetOriginUrl() + "/_framework/blazor.boot.json";

await executor.Execute(async () =>
Expand All @@ -648,9 +693,9 @@ await executor.Execute(async () =>
private static string HelloJson = "{'hello':'world'}".Replace('\'', '"');
private static string EchoStart = "{\"Method\":\"POST\",\"Url\":\"/Echo.ashx";

private Task HttpClient_ActionInDifferentThread(string url, Executor executor1, Executor executor2, Func<HttpResponseMessage, Task> e2Job)
private async Task HttpClient_ActionInDifferentThread(string url, Executor executor1, Executor executor2, Func<HttpResponseMessage, Task> e2Job)
{
var cts = CreateTestCaseTimeoutSource();
using var cts = CreateTestCaseTimeoutSource();

var e1Job = async (Task e2done, TaskCompletionSource<HttpResponseMessage> e1State) =>
{
Expand All @@ -669,7 +714,7 @@ private Task HttpClient_ActionInDifferentThread(string url, Executor executor1,

await e2done;
};
return ActionsInDifferentThreads<HttpResponseMessage>(executor1, executor2, e1Job, e2Job, cts);
await ActionsInDifferentThreads<HttpResponseMessage>(executor1, executor2, e1Job, e2Job, cts);
}

[Theory, MemberData(nameof(GetTargetThreads2x))]
Expand Down
Loading
Loading