diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs index 4b3dfb3be1cc4..e548aa3e8e270 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs @@ -31,6 +31,9 @@ private void DisposeCore() private void AcquireCore() { +#if FEATURE_WASM_MANAGED_THREADS + Thread.AssureBlockingPossible(); +#endif Interop.Sys.LowLevelMonitor_Acquire(_nativeMonitor); } @@ -41,6 +44,9 @@ private void ReleaseCore() private void WaitCore() { +#if FEATURE_WASM_MANAGED_THREADS + Thread.AssureBlockingPossible(); +#endif Interop.Sys.LowLevelMonitor_Wait(_nativeMonitor); } @@ -54,6 +60,10 @@ private bool WaitCore(int timeoutMilliseconds) return true; } +#if FEATURE_WASM_MANAGED_THREADS + Thread.AssureBlockingPossible(); +#endif + return Interop.Sys.LowLevelMonitor_TimedWait(_nativeMonitor, timeoutMilliseconds); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs index 28c2c5c0f06c1..3a4fc91d990d9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs @@ -366,15 +366,18 @@ await executor.Execute(async () => TaskCompletionSource tcs = new TaskCompletionSource(); Console.WriteLine("ThreadingTimer: Start Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId); - using var timer = new Timer(_ => + await ForceBlockingWaitAsync(async () => { - Assert.NotEqual(1, Environment.CurrentManagedThreadId); - Assert.True(Thread.CurrentThread.IsThreadPoolThread); - hit = true; - tcs.SetResult(); - }, null, 100, Timeout.Infinite); - - await tcs.Task; + using var timer = new Timer(_ => + { + Assert.NotEqual(1, Environment.CurrentManagedThreadId); + Assert.True(Thread.CurrentThread.IsThreadPoolThread); + hit = true; + tcs.SetResult(); + }, null, 100, Timeout.Infinite); + + await tcs.Task; + }); }, cts.Token); Console.WriteLine("ThreadingTimer: End Time: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId); @@ -411,6 +414,22 @@ await executor.Execute(async () => }, cts.Token); } + static async Task ForceBlockingWaitAsync(Func action) + { + var flag = Thread.ThrowOnBlockingWaitOnJSInteropThread; + try + { + Thread.ThrowOnBlockingWaitOnJSInteropThread = false; + + await action(); + } + finally + { + Thread.ThrowOnBlockingWaitOnJSInteropThread = flag; + } + } + + [Theory, MemberData(nameof(GetTargetThreads))] public async Task ManagedDelay_ContinueWith(Executor executor) { @@ -418,10 +437,13 @@ public async Task ManagedDelay_ContinueWith(Executor executor) using var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { - await Task.Delay(10, cts.Token).ContinueWith(_ => + await ForceBlockingWaitAsync(async () => { - hit = true; - }, TaskContinuationOptions.ExecuteSynchronously); + await Task.Delay(10, cts.Token).ContinueWith(_ => + { + hit = true; + }, TaskContinuationOptions.ExecuteSynchronously); + }); }, cts.Token); Assert.True(hit); } @@ -432,7 +454,10 @@ public async Task ManagedDelay_ConfigureAwait_True(Executor executor) using var cts = CreateTestCaseTimeoutSource(); await executor.Execute(async () => { - await Task.Delay(10, cts.Token).ConfigureAwait(true); + await ForceBlockingWaitAsync(async () => + { + await Task.Delay(10, cts.Token).ConfigureAwait(true); + }); executor.AssertAwaitCapturedContext(); }, cts.Token); @@ -441,23 +466,32 @@ await executor.Execute(async () => [Theory, MemberData(nameof(GetTargetThreadsAndBlockingCalls))] public async Task WaitAssertsOnJSInteropThreads(Executor executor, NamedCall method) { - using var cts = CreateTestCaseTimeoutSource(); - await executor.Execute(Task () => + CancellationTokenSource? cts = null; + try { - Exception? exception = null; - try - { - method.Call(cts.Token); - } - catch (Exception ex) + Thread.ForceBlockingWait((_) => cts = CreateTestCaseTimeoutSource(), null); + await executor.Execute(Task () => { - exception = ex; - } - Console.WriteLine("WaitAssertsOnJSInteropThreads: ExecuterType: " + executor.Type + " ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId); - executor.AssertBlockingWait(exception); + Exception? exception = null; + try + { + method.Call(cts.Token); + } + catch (Exception ex) + { + exception = ex; + } - return Task.CompletedTask; - }, cts.Token); + Console.WriteLine("WaitAssertsOnJSInteropThreads: ExecuterType: " + executor.Type + " ManagedThreadId: " + Environment.CurrentManagedThreadId + " NativeThreadId: " + WebWorkerTestHelper.NativeThreadId); + executor.AssertBlockingWait(exception); + + return Task.CompletedTask; + }, cts.Token); + } + finally + { + cts?.Dispose(); + } } [Theory, MemberData(nameof(GetTargetThreads))] diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestBase.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestBase.cs index 87f88745377b0..5c84c2766b87e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestBase.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestBase.cs @@ -146,6 +146,20 @@ public class NamedCall override public string ToString() => Name; } + static void LocalCtsIgnoringCall(Action action) + { + var cts = new CancellationTokenSource(8); + try { + action(cts.Token); + } catch (OperationCanceledException exception) { + if (exception.CancellationToken != cts.Token) + { + throw; + } + /* ignore the local one */ + } + } + public static IEnumerable BlockingCalls = new List { new NamedCall { Name = "Task.Wait", Call = delegate (CancellationToken ct) { Task.Delay(10, ct).Wait(ct); }}, @@ -153,17 +167,27 @@ public class NamedCall new NamedCall { Name = "Task.WaitAny", Call = delegate (CancellationToken ct) { Task.WaitAny(Task.Delay(10, ct)); }}, new NamedCall { Name = "ManualResetEventSlim.Wait", Call = delegate (CancellationToken ct) { using var mr = new ManualResetEventSlim(false); - using var cts = new CancellationTokenSource(8); - try { - mr.Wait(cts.Token); - } catch (OperationCanceledException) { /* ignore */ } + LocalCtsIgnoringCall(mr.Wait); }}, new NamedCall { Name = "SemaphoreSlim.Wait", Call = delegate (CancellationToken ct) { using var sem = new SemaphoreSlim(2); - var cts = new CancellationTokenSource(8); - try { - sem.Wait(cts.Token); - } catch (OperationCanceledException) { /* ignore */ } + LocalCtsIgnoringCall(sem.Wait); + }}, + new NamedCall { Name = "CancellationTokenSource.ctor", Call = delegate (CancellationToken ct) { + using var cts = new CancellationTokenSource(8); + }}, + new NamedCall { Name = "Mutex.WaitOne", Call = delegate (CancellationToken ct) { + using var mr = new ManualResetEventSlim(false); + var mutex = new Mutex(); + var thread = new Thread(() => { + mutex.WaitOne(); + mr.Set(); + Thread.Sleep(50); + mutex.ReleaseMutex(); + }); + thread.Start(); + Thread.ForceBlockingWait((_) => mr.Wait(), null); + mutex.WaitOne(); }}, };