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

[wasm][mt] Guard more places for blocking wait on JS interop threads #98508

Closed
Changes from 1 commit
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 @@ -365,15 +365,19 @@ public async Task ThreadingTimer(Executor executor)
{
TaskCompletionSource tcs = new TaskCompletionSource();

using var timer = new Timer(_ =>
await ForceBlockingWaitAsync(async () =>
{
Assert.NotEqual(1, Environment.CurrentManagedThreadId);
Assert.True(Thread.CurrentThread.IsThreadPoolThread);
tcs.SetResult();
hit = true;
}, null, 100, Timeout.Infinite);

await tcs.Task;
using var timer = new Timer(_ =>
{
Assert.NotEqual(1, Environment.CurrentManagedThreadId);
Assert.True(Thread.CurrentThread.IsThreadPoolThread);
tcs.SetResult();
hit = true;
}, null, 100, Timeout.Infinite);

await tcs.Task;
});
}, cts.Token);

Assert.True(hit);
Expand Down Expand Up @@ -409,17 +413,36 @@ public async Task JSDelay_ConfigureAwait_True(Executor executor)
}, cts.Token);
}

static async Task ForceBlockingWaitAsync(Func<Task> 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)
{
var hit = false;
using var cts = CreateTestCaseTimeoutSource();
await executor.Execute(async () =>
{
await Task.Delay(10, cts.Token).ContinueWith(_ =>
await ForceBlockingWaitAsync(async () =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree that making Task.Delay forbidden operation on UI thread is a way forward.
I think we have to find a way that it never calls blocking .Wait

Here you are just hiding the problem from the unit test, rigth ?

Copy link
Member

@pavelsavara pavelsavara Feb 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the same for Timer, there is no theoretical reason why this needs to block.
Except maybe for creating new child thread. If that's the reason, we should add ForceBlockingWaitAsync into new Thread() code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree that making Task.Delay forbidden operation on UI thread is a way forward. I think we have to find a way that it never calls blocking .Wait

Here you are just hiding the problem from the unit test, rigth ?

Yes, here I am forcing it to not break the current ManagedDelay_ContinueWith test.

The Task.Delay eventually ends calling System.Threading.LowLevelMonitor.AcquireCore(), which calls to native pthread_mutex_lock - that is a blocking call.

The Timer calls LowLevelMonitor.AcquireCore as well.

Details:

  info: System.PlatformNotSupportedException : Blocking wait is not supported on the JS interop threads.
  info:    at System.Threading.Thread.AssureBlockingPossible() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs:line 683
  info:    at System.Threading.LowLevelMonitor.AcquireCore() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs:line 35
  info:    at System.Threading.LowLevelMonitor.Acquire() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs:line 78
  info:    at System.Threading.WaitSubsystem.ThreadWaitInfo.TrySignalToSatisfyWait(WaitedListNode , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs:line 448
  info:    at System.Threading.WaitSubsystem.WaitableObject.SignalAutoResetEvent() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs:line 709
  info:    at System.Threading.WaitSubsystem.WaitableObject.SignalEvent(LockHolder& ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs:line 658
  info:    at System.Threading.WaitSubsystem.SetEvent(WaitableObject ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs:line 254
  info:    at System.Threading.WaitSubsystem.SetEvent(IntPtr ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs:line 244
  info:    at System.Threading.EventWaitHandle.Set() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs:line 48
  info:    at System.Threading.TimerQueue.SetTimerPortable(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs:line 66
  info:    at System.Threading.TimerQueue.SetTimer(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs:line 16
  info:    at System.Threading.TimerQueue.EnsureTimerFiresBy(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 138
  info:    at System.Threading.TimerQueue.UpdateTimer(TimerQueueTimer , UInt32 , UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 368
  info:    at System.Threading.TimerQueueTimer.Change(UInt32 , UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 561
  info:    at System.Threading.TimerQueueTimer..ctor(TimerCallback , Object , UInt32 , UInt32 , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 516
  info:    at System.Threading.Tasks.Task.DelayPromise..ctor(UInt32 , TimeProvider ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:line 5704
  info:    at System.Threading.Tasks.Task.DelayPromiseWithCancellation..ctor(UInt32 , TimeProvider , CancellationToken ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:line 5749
  info:    at System.Threading.Tasks.Task.Delay(UInt32 , TimeProvider , CancellationToken ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:line 5668
  info:    at System.Threading.Tasks.Task.Delay(Int32 millisecondsDelay, CancellationToken cancellationToken) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:line 5664
  info:    at System.Runtime.InteropServices.JavaScript.Tests.WebWorkerTest.<>c__DisplayClass15_0.<<ManagedDelay_ContinueWith>b__0>d.MoveNext() in /Users/rodo/git/threads-runtime/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs:line 442
  info: [FAIL] System.Runtime.InteropServices.JavaScript.Tests.WebWorkerTest.ThreadingTimer(executor: Main)
  info: System.PlatformNotSupportedException : Blocking wait is not supported on the JS interop threads.
  info:    at System.Threading.Thread.AssureBlockingPossible() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs:line 683
  info:    at System.Threading.LowLevelMonitor.AcquireCore() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs:line 35
  info:    at System.Threading.LowLevelMonitor.Acquire() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs:line 78
  info:    at System.Threading.WaitSubsystem.ThreadWaitInfo.TrySignalToSatisfyWait(WaitedListNode , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs:line 448
  info:    at System.Threading.WaitSubsystem.WaitableObject.SignalAutoResetEvent() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs:line 709
  info:    at System.Threading.WaitSubsystem.WaitableObject.SignalEvent(LockHolder& ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs:line 658
  info:    at System.Threading.WaitSubsystem.SetEvent(WaitableObject ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs:line 254
  info:    at System.Threading.WaitSubsystem.SetEvent(IntPtr ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs:line 244
  info:    at System.Threading.EventWaitHandle.Set() in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs:line 48
  info:    at System.Threading.TimerQueue.SetTimerPortable(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs:line 66
  info:    at System.Threading.TimerQueue.SetTimer(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs:line 16
  info:    at System.Threading.TimerQueue.EnsureTimerFiresBy(UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 138
  info:    at System.Threading.TimerQueue.UpdateTimer(TimerQueueTimer , UInt32 , UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 368
  info:    at System.Threading.TimerQueueTimer.Change(UInt32 , UInt32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 561
  info:    at System.Threading.TimerQueueTimer..ctor(TimerCallback , Object , UInt32 , UInt32 , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 516
  info:    at System.Threading.Timer.TimerSetup(TimerCallback , Object , UInt32 , UInt32 , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 905
  info:    at System.Threading.Timer..ctor(TimerCallback , Object , Int32 , Int32 , Boolean ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 845
  info:    at System.Threading.Timer..ctor(TimerCallback , Object , Int32 , Int32 ) in /Users/rodo/git/threads-runtime/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs:line 832
  info:    at System.Runtime.InteropServices.JavaScript.Tests.WebWorkerTest.<>c__DisplayClass11_0.<<ThreadingTimer>b__0>d.MoveNext() in /Users/rodo/git/threads-runtime/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs:line 371

{
hit = true;
}, TaskContinuationOptions.ExecuteSynchronously);
await Task.Delay(10, cts.Token).ContinueWith(_ =>
{
hit = true;
}, TaskContinuationOptions.ExecuteSynchronously);
});
}, cts.Token);
Assert.True(hit);
}
Expand All @@ -430,7 +453,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);
Expand Down
Loading