From 8f49ee671b973596442bfd6e6d68b3d88e033456 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 12:52:36 +0200 Subject: [PATCH 1/8] fix https://github.com/dotnet/runtime/issues/104803 --- .../src/System/Threading/Thread.Unix.cs | 2 +- .../System/Threading/ThreadPoolWorkQueue.cs | 3 --- .../System/Threading/Wasi/WasiEventLoop.cs | 26 ++++++++++--------- .../System/Threading/ThreadPool.Wasi.Mono.cs | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index a3302b3bedc38..4411103cbf94a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -22,7 +22,7 @@ internal static int PollWasiEventLoopUntilResolved(Task mainTask) { while (!mainTask.IsCompleted) { - WasiEventLoop.DispatchWasiEventLoop(); + ThreadPoolWorkQueue.Dispatch(); } var exception = mainTask.Exception; if (exception is not null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 279932486e58a..69a59198a40f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -906,10 +906,7 @@ internal static bool Dispatch() // thread because it sees a Determining or Scheduled stage, and the current thread is the last thread processing // work items, the current thread must either see the work item queued by the enqueuer, or it must see a stage of // Scheduled, and try to dequeue again or request another thread. -#if !TARGET_WASI - // TODO https://github.com/dotnet/runtime/issues/104803 Debug.Assert(workQueue._separated.queueProcessingStage == QueueProcessingStage.Scheduled); -#endif workQueue._separated.queueProcessingStage = QueueProcessingStage.Determining; Interlocked.MemoryBarrier(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index e8e7b20dc3473..b8ea889d3e0c5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -14,7 +14,7 @@ internal static class WasiEventLoop // it will be leaked and stay in this list forever. // it will also keep the Pollable handle alive and prevent it from being disposed private static readonly List s_pollables = new(); - private static bool s_tasksCanceled; + private static bool s_checkScheduled; internal static Task RegisterWasiPollableHandle(int handle, CancellationToken cancellationToken) { @@ -29,18 +29,24 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c // this will register the pollable holder into s_pollables var holder = new PollableHolder(pollable, cancellationToken); s_pollables.Add(holder); + + ScheduleCheck(); + return holder.taskCompletionSource.Task; } - // this is not thread safe - internal static void DispatchWasiEventLoop() + internal static void ScheduleCheck() { - ThreadPoolWorkQueue.Dispatch(); - if (s_tasksCanceled) + if (!s_checkScheduled && s_pollables.Count > 0) { - s_tasksCanceled = false; - return; + s_checkScheduled = true; + ThreadPool.UnsafeQueueUserWorkItem(BlockOnPollables, null); } + } + + internal static void BlockOnPollables(object? _) + { + s_checkScheduled = false; var holders = new List(s_pollables.Count); var pending = new List(s_pollables.Count); @@ -73,6 +79,7 @@ internal static void DispatchWasiEventLoop() s_pollables.Add(holder); } } + ScheduleCheck(); } } @@ -120,11 +127,6 @@ private static void CancelAndDispose(object? s) return; } - // Tell event loop to exit early, giving the application a - // chance to quit if the task(s) it is interested in have - // completed. - s_tasksCanceled = true; - // it will be removed from s_pollables on the next run self.isDisposed = true; self.pollable.Dispose(); diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs index bad9fbdbaaddf..0d082fd863cb9 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs @@ -35,7 +35,7 @@ public static partial class ThreadPool { // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that // the runtime may use the thread for processing other work - internal static bool YieldFromDispatchLoop => false; + internal static bool YieldFromDispatchLoop => true; private const bool IsWorkerTrackingEnabledInConfig = false; From 9a2f9d4e1a1aba45dd6721a3c9440cefd0349159 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 15:27:50 +0200 Subject: [PATCH 2/8] PollWasiEventLoopUntilResolvedVoid --- .../Common/tests/WasmTestRunner/WasmTestRunner.cs | 2 +- .../src/System/Threading/Thread.Unix.cs | 14 +++++++++++++- src/mono/sample/wasi/http-p2/Program.cs | 2 +- src/mono/wasi/testassets/Http.cs | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs index 1cf1b35cf291a..8c393b95f69c2 100644 --- a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs +++ b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs @@ -20,7 +20,7 @@ public static int Main(string[] args) return PollWasiEventLoopUntilResolved((Thread)null!, MainAsync(args)); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "PollWasiEventLoopUntilResolved")] - static extern int PollWasiEventLoopUntilResolved(Thread t, Task mainTask); + static extern T PollWasiEventLoopUntilResolved(Thread t, Task mainTask); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index 4411103cbf94a..b9111c6d1fa6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -18,7 +18,7 @@ internal static System.Threading.Tasks.Task RegisterWasiPollableHandle(int handl return WasiEventLoop.RegisterWasiPollableHandle(handle, cancellationToken); } - internal static int PollWasiEventLoopUntilResolved(Task mainTask) + internal static T PollWasiEventLoopUntilResolved(Task mainTask) { while (!mainTask.IsCompleted) { @@ -33,6 +33,18 @@ internal static int PollWasiEventLoopUntilResolved(Task mainTask) return mainTask.Result; } + internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) + { + while (!mainTask.IsCompleted) + { + ThreadPoolWorkQueue.Dispatch(); + } + var exception = mainTask.Exception; + if (exception is not null) + { + throw exception; + } + } #endif // the closest analog to Sleep(0) on Unix is sched_yield diff --git a/src/mono/sample/wasi/http-p2/Program.cs b/src/mono/sample/wasi/http-p2/Program.cs index e940c0a4100b6..a6a4efca4c0b2 100644 --- a/src/mono/sample/wasi/http-p2/Program.cs +++ b/src/mono/sample/wasi/http-p2/Program.cs @@ -78,7 +78,7 @@ public static int Main(string[] args) return PollWasiEventLoopUntilResolved((Thread)null!, MainAsync(args)); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "PollWasiEventLoopUntilResolved")] - static extern int PollWasiEventLoopUntilResolved(Thread t, Task mainTask); + static extern T PollWasiEventLoopUntilResolved(Thread t, Task mainTask); } } diff --git a/src/mono/wasi/testassets/Http.cs b/src/mono/wasi/testassets/Http.cs index 49bc33f28fa3c..87b1f8437f71f 100644 --- a/src/mono/wasi/testassets/Http.cs +++ b/src/mono/wasi/testassets/Http.cs @@ -83,6 +83,6 @@ public static int Main(string[] args) return PollWasiEventLoopUntilResolved((Thread)null!, MainAsync(args)); [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "PollWasiEventLoopUntilResolved")] - static extern int PollWasiEventLoopUntilResolved(Thread t, Task mainTask); + static extern T PollWasiEventLoopUntilResolved(Thread t, Task mainTask); } } From a1cf17a64b7347a68fdb331f595799eb6daf1983 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 16:20:32 +0200 Subject: [PATCH 3/8] more --- .../src/System/Threading/Thread.Unix.cs | 8 ++++++++ .../src/System/Threading/Wasi/WasiEventLoop.cs | 11 ++++++++++- src/mono/sample/wasi/http-p2/Program.cs | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index b9111c6d1fa6e..6644ee72f45a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -24,6 +24,10 @@ internal static T PollWasiEventLoopUntilResolved(Task mainTask) { ThreadPoolWorkQueue.Dispatch(); } + + // because next call to PollInterop.Poll() could block for long time + WasiEventLoop.CancelAllPollables(); + var exception = mainTask.Exception; if (exception is not null) { @@ -39,6 +43,10 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) { ThreadPoolWorkQueue.Dispatch(); } + + // because next call to PollInterop.Poll() could block for long time + WasiEventLoop.CancelAllPollables(); + var exception = mainTask.Exception; if (exception is not null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index b8ea889d3e0c5..70a11ff7cd607 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -35,6 +35,15 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c return holder.taskCompletionSource.Task; } + internal static void CancelAllPollables() + { + for (int i = 0; i < s_pollables.Count; i++) + { + PollableHolder.CancelAndDispose(s_pollables[i]); + } + s_pollables.Clear(); + } + internal static void ScheduleCheck() { if (!s_checkScheduled && s_pollables.Count > 0) @@ -119,7 +128,7 @@ public void ResolveAndDispose() } // for GC of abandoned Tasks or for cancellation - private static void CancelAndDispose(object? s) + public static void CancelAndDispose(object? s) { PollableHolder self = (PollableHolder)s!; if (self.isDisposed) diff --git a/src/mono/sample/wasi/http-p2/Program.cs b/src/mono/sample/wasi/http-p2/Program.cs index a6a4efca4c0b2..6858fb7795a5f 100644 --- a/src/mono/sample/wasi/http-p2/Program.cs +++ b/src/mono/sample/wasi/http-p2/Program.cs @@ -13,6 +13,7 @@ public static class WasiMainWrapper { public static async Task MainAsync(string[] args) { + var never = Task.Delay(100_000_000); await Task.Delay(100); GC.Collect(); // test that Pollable->Task is not collected until resolved From d489456eacfec86eca87313775c92787c2029b9b Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 16:46:28 +0200 Subject: [PATCH 4/8] wip --- .../src/System/Threading/Thread.Unix.cs | 32 ++--------------- .../System/Threading/ThreadPoolWorkQueue.cs | 6 +++- .../System/Threading/Wasi/WasiEventLoop.cs | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index 6644ee72f45a2..592f0244e011a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -20,40 +20,14 @@ internal static System.Threading.Tasks.Task RegisterWasiPollableHandle(int handl internal static T PollWasiEventLoopUntilResolved(Task mainTask) { - while (!mainTask.IsCompleted) - { - ThreadPoolWorkQueue.Dispatch(); - } - - // because next call to PollInterop.Poll() could block for long time - WasiEventLoop.CancelAllPollables(); - - var exception = mainTask.Exception; - if (exception is not null) - { - throw exception; - } - - return mainTask.Result; + return WasiEventLoop.PollWasiEventLoopUntilResolved(mainTask); } internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) { - while (!mainTask.IsCompleted) - { - ThreadPoolWorkQueue.Dispatch(); - } - - // because next call to PollInterop.Poll() could block for long time - WasiEventLoop.CancelAllPollables(); - - var exception = mainTask.Exception; - if (exception is not null) - { - throw exception; - } + WasiEventLoop.PollWasiEventLoopUntilResolvedVoid(mainTask); } -#endif +#endif // TARGET_WASI // the closest analog to Sleep(0) on Unix is sched_yield internal static void UninterruptibleSleep0() => Thread.Yield(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 69a59198a40f4..30db7357b031f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1127,7 +1127,11 @@ internal static bool Dispatch() } // Check if the dispatch quantum has expired - if ((uint)(currentTickCount - startTickCount) < DispatchQuantumMs) + if ((uint)(currentTickCount - startTickCount) < DispatchQuantumMs +#if TARGET_WASI + && !WasiEventLoop.s_yieldFromDispatchLoopWhen?.IsCompleted +#endif + ) { continue; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index 70a11ff7cd607..359703b58a0a2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -15,6 +15,7 @@ internal static class WasiEventLoop // it will also keep the Pollable handle alive and prevent it from being disposed private static readonly List s_pollables = new(); private static bool s_checkScheduled; + internal static Task? s_yieldFromDispatchLoopWhen = null; internal static Task RegisterWasiPollableHandle(int handle, CancellationToken cancellationToken) { @@ -35,6 +36,39 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c return holder.taskCompletionSource.Task; } + + internal static T PollWasiEventLoopUntilResolved(Task mainTask) + { + s_yieldFromDispatchLoopWhen = mainTask; + while (!mainTask.IsCompleted) + { + ThreadPoolWorkQueue.Dispatch(); + } + + var exception = mainTask.Exception; + if (exception is not null) + { + throw exception; + } + + return mainTask.Result; + } + + internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) + { + s_yieldFromDispatchLoopWhen = mainTask; + while (!mainTask.IsCompleted) + { + ThreadPoolWorkQueue.Dispatch(); + } + + var exception = mainTask.Exception; + if (exception is not null) + { + throw exception; + } + } + internal static void CancelAllPollables() { for (int i = 0; i < s_pollables.Count; i++) From 5f8383bfda10c2dff5c5c68555ae04d8beffdc98 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 18:39:36 +0200 Subject: [PATCH 5/8] CPU-bound work to do --- .../System/Threading/ThreadPoolWorkQueue.cs | 6 +--- .../System/Threading/Wasi/WasiEventLoop.cs | 32 +++++++++++++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs index 30db7357b031f..69a59198a40f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPoolWorkQueue.cs @@ -1127,11 +1127,7 @@ internal static bool Dispatch() } // Check if the dispatch quantum has expired - if ((uint)(currentTickCount - startTickCount) < DispatchQuantumMs -#if TARGET_WASI - && !WasiEventLoop.s_yieldFromDispatchLoopWhen?.IsCompleted -#endif - ) + if ((uint)(currentTickCount - startTickCount) < DispatchQuantumMs) { continue; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index 359703b58a0a2..abedded0f29e3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using WasiPollWorld.wit.imports.wasi.io.v0_2_1; using Pollable = WasiPollWorld.wit.imports.wasi.io.v0_2_1.IPoll.Pollable; +using MonotonicClockInterop = WasiPollWorld.wit.imports.wasi.clocks.v0_2_1.MonotonicClockInterop; namespace System.Threading { @@ -15,7 +16,7 @@ internal static class WasiEventLoop // it will also keep the Pollable handle alive and prevent it from being disposed private static readonly List s_pollables = new(); private static bool s_checkScheduled; - internal static Task? s_yieldFromDispatchLoopWhen = null; + private static Pollable? s_resolvedPollable; internal static Task RegisterWasiPollableHandle(int handle, CancellationToken cancellationToken) { @@ -39,7 +40,6 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c internal static T PollWasiEventLoopUntilResolved(Task mainTask) { - s_yieldFromDispatchLoopWhen = mainTask; while (!mainTask.IsCompleted) { ThreadPoolWorkQueue.Dispatch(); @@ -56,7 +56,6 @@ internal static T PollWasiEventLoopUntilResolved(Task mainTask) internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) { - s_yieldFromDispatchLoopWhen = mainTask; while (!mainTask.IsCompleted) { ThreadPoolWorkQueue.Dispatch(); @@ -71,6 +70,9 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) internal static void CancelAllPollables() { + s_resolvedPollable?.Dispose(); + s_resolvedPollable = null; + for (int i = 0; i < s_pollables.Count; i++) { PollableHolder.CancelAndDispose(s_pollables[i]); @@ -83,11 +85,11 @@ internal static void ScheduleCheck() if (!s_checkScheduled && s_pollables.Count > 0) { s_checkScheduled = true; - ThreadPool.UnsafeQueueUserWorkItem(BlockOnPollables, null); + ThreadPool.UnsafeQueueUserWorkItem(CheckPollables, null); } } - internal static void BlockOnPollables(object? _) + internal static void CheckPollables(object? _) { s_checkScheduled = false; @@ -107,13 +109,28 @@ internal static void BlockOnPollables(object? _) if (pending.Count > 0) { + var resolvedPollableIndex = -1; + // if there is CPU-bound work to do, we should not block on PollInterop.Poll below + // so we will append pollable resolved in 0ms + // in effect, the PollInterop.Poll would not block us + if (ThreadPool.PendingWorkItemCount > 0) + { + s_resolvedPollable ??= MonotonicClockInterop.SubscribeDuration(0); + resolvedPollableIndex = pending.Count; + pending.Add(s_resolvedPollable); + } + var readyIndexes = PollInterop.Poll(pending); for (int i = 0; i < readyIndexes.Length; i++) { uint readyIndex = readyIndexes[i]; - var holder = holders[(int)readyIndex]; - holder.ResolveAndDispose(); + if (resolvedPollableIndex != readyIndex) + { + var holder = holders[(int)readyIndex]; + holder.ResolveAndDispose(); + } } + for (int i = 0; i < holders.Count; i++) { PollableHolder holder = holders[i]; @@ -122,6 +139,7 @@ internal static void BlockOnPollables(object? _) s_pollables.Add(holder); } } + ScheduleCheck(); } } From 1cb18c9b273765085145bee41026b74bb82539ed Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Thu, 5 Sep 2024 21:58:37 +0200 Subject: [PATCH 6/8] fix exit --- .../src/System/Threading/Wasi/WasiEventLoop.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index abedded0f29e3..1dcff611af583 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -17,6 +17,7 @@ internal static class WasiEventLoop private static readonly List s_pollables = new(); private static bool s_checkScheduled; private static Pollable? s_resolvedPollable; + private static Task? s_mainTask; internal static Task RegisterWasiPollableHandle(int handle, CancellationToken cancellationToken) { @@ -40,10 +41,12 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c internal static T PollWasiEventLoopUntilResolved(Task mainTask) { + s_mainTask = mainTask; while (!mainTask.IsCompleted) { ThreadPoolWorkQueue.Dispatch(); } + s_mainTask = null; var exception = mainTask.Exception; if (exception is not null) @@ -56,10 +59,12 @@ internal static T PollWasiEventLoopUntilResolved(Task mainTask) internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) { + s_mainTask = mainTask; while (!mainTask.IsCompleted) { ThreadPoolWorkQueue.Dispatch(); } + s_mainTask = null; var exception = mainTask.Exception; if (exception is not null) @@ -113,7 +118,7 @@ internal static void CheckPollables(object? _) // if there is CPU-bound work to do, we should not block on PollInterop.Poll below // so we will append pollable resolved in 0ms // in effect, the PollInterop.Poll would not block us - if (ThreadPool.PendingWorkItemCount > 0) + if (ThreadPool.PendingWorkItemCount > 0 || (s_mainTask != null && s_mainTask.IsCompleted)) { s_resolvedPollable ??= MonotonicClockInterop.SubscribeDuration(0); resolvedPollableIndex = pending.Count; From d39e0f835e1affe05e5322c509d0c31473c46613 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 9 Sep 2024 20:36:38 +0200 Subject: [PATCH 7/8] Update src/mono/sample/wasi/http-p2/Program.cs Co-authored-by: Larry Ewing --- src/mono/sample/wasi/http-p2/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/sample/wasi/http-p2/Program.cs b/src/mono/sample/wasi/http-p2/Program.cs index 6858fb7795a5f..9bf6140161db5 100644 --- a/src/mono/sample/wasi/http-p2/Program.cs +++ b/src/mono/sample/wasi/http-p2/Program.cs @@ -13,7 +13,7 @@ public static class WasiMainWrapper { public static async Task MainAsync(string[] args) { - var never = Task.Delay(100_000_000); + _ = Task.Delay(100_000_000); // create a task that will not complete before main await Task.Delay(100); GC.Collect(); // test that Pollable->Task is not collected until resolved From 3d94112527e51b7f6f2ace43a2ea9e3329e233a2 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 9 Sep 2024 20:44:46 +0200 Subject: [PATCH 8/8] feedback --- .../System/Threading/Wasi/WasiEventLoop.cs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs index 1dcff611af583..f2a5d898566cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiEventLoop.cs @@ -41,13 +41,18 @@ internal static Task RegisterWasiPollable(Pollable pollable, CancellationToken c internal static T PollWasiEventLoopUntilResolved(Task mainTask) { - s_mainTask = mainTask; - while (!mainTask.IsCompleted) + try { - ThreadPoolWorkQueue.Dispatch(); + s_mainTask = mainTask; + while (!mainTask.IsCompleted) + { + ThreadPoolWorkQueue.Dispatch(); + } + } + finally + { + s_mainTask = null; } - s_mainTask = null; - var exception = mainTask.Exception; if (exception is not null) { @@ -59,12 +64,18 @@ internal static T PollWasiEventLoopUntilResolved(Task mainTask) internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) { - s_mainTask = mainTask; - while (!mainTask.IsCompleted) + try + { + s_mainTask = mainTask; + while (!mainTask.IsCompleted) + { + ThreadPoolWorkQueue.Dispatch(); + } + } + finally { - ThreadPoolWorkQueue.Dispatch(); + s_mainTask = null; } - s_mainTask = null; var exception = mainTask.Exception; if (exception is not null) @@ -73,18 +84,6 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) } } - internal static void CancelAllPollables() - { - s_resolvedPollable?.Dispose(); - s_resolvedPollable = null; - - for (int i = 0; i < s_pollables.Count; i++) - { - PollableHolder.CancelAndDispose(s_pollables[i]); - } - s_pollables.Clear(); - } - internal static void ScheduleCheck() { if (!s_checkScheduled && s_pollables.Count > 0)