diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 5e65109ddbb378..49b0e33a86f9b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2657,7 +2657,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs index 3c5888a6f80dbd..c46c43604bb1df 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.Unix.cs @@ -16,11 +16,9 @@ public static partial class ThreadPool AppContextConfigHelper.GetBooleanConfig("System.Threading.ThreadPool.EnableWorkerTracking", "DOTNET_ThreadPool_EnableWorkerTracking"); #endif -#if !(TARGET_BROWSER && FEATURE_WASM_THREADS) // 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; -#endif #if !CORECLR internal static bool EnsureConfigInitialized() => true; diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 81e577b690429f..ca865e7a856dad 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -273,13 +273,10 @@ - + - - - diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs index 632b0c934ee4c0..cc3f606fe6273d 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs @@ -7,7 +7,7 @@ internal sealed partial class PortableThreadPool { private static partial class WorkerThread { - private static bool IsIOPending => WebWorkerEventLoop.HasJavaScriptInteropDependents; + private static bool IsIOPending => false; } private struct CpuUtilizationReader diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.Browser.Threads.Mono.cs deleted file mode 100644 index b45dee7fa2fd6f..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.Browser.Threads.Mono.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; -using System.Runtime.CompilerServices; - -namespace System.Threading -{ - internal sealed partial class PortableThreadPool - { - /// - /// The worker thread infastructure for the CLR thread pool. - /// - private static partial class WorkerThread - { - /// - /// Semaphore for controlling how many threads are currently working. - /// - private static readonly LowLevelLifoAsyncWaitSemaphore s_semaphore = - new LowLevelLifoAsyncWaitSemaphore( - 0, - MaxPossibleThreadCount, - AppContextConfigHelper.GetInt32Config( - "System.Threading.ThreadPool.UnfairSemaphoreSpinLimit", - SemaphoreSpinCountDefault, - false), - onWait: () => - { - if (NativeRuntimeEventSource.Log.IsEnabled()) - { - NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadWait( - (uint)ThreadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); - } - }); - - private static readonly ThreadStart s_workerThreadStart = WorkerThreadStart; - - private sealed record SemaphoreWaitState(PortableThreadPool ThreadPoolInstance, LowLevelLock ThreadAdjustmentLock, WebWorkerEventLoop.KeepaliveToken KeepaliveToken) - { - public bool SpinWait = true; - - public void ResetIteration() { - SpinWait = true; - } - } - - private static void WorkerThreadStart() - { - Thread.CurrentThread.SetThreadPoolWorkerThreadName(); - - PortableThreadPool threadPoolInstance = ThreadPoolInstance; - - if (NativeRuntimeEventSource.Log.IsEnabled()) - { - NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStart( - (uint)threadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); - } - - LowLevelLock threadAdjustmentLock = threadPoolInstance._threadAdjustmentLock; - var keepaliveToken = WebWorkerEventLoop.KeepalivePush(); - SemaphoreWaitState state = new(threadPoolInstance, threadAdjustmentLock, keepaliveToken) { SpinWait = true }; - // set up the callbacks for semaphore waits, tell - // emscripten to keep the thread alive, and return to - // the JS event loop. - WaitForWorkLoop(s_semaphore, state); - // return from thread start with keepalive - the thread will stay alive in the JS event loop - } - - private static readonly Action s_WorkLoopSemaphoreSuccess = new(WorkLoopSemaphoreSuccess); - private static readonly Action s_WorkLoopSemaphoreTimedOut = new(WorkLoopSemaphoreTimedOut); - - private static void WaitForWorkLoop(LowLevelLifoAsyncWaitSemaphore semaphore, SemaphoreWaitState state) - { - semaphore.PrepareAsyncWait(ThreadPoolThreadTimeoutMs, s_WorkLoopSemaphoreSuccess, s_WorkLoopSemaphoreTimedOut, state); - // thread should still be kept alive - Debug.Assert(state.KeepaliveToken.Valid); - } - - private static void WorkLoopSemaphoreSuccess(LowLevelLifoAsyncWaitSemaphore semaphore, object? stateObject) - { - SemaphoreWaitState state = (SemaphoreWaitState)stateObject!; - WorkerDoWork(state.ThreadPoolInstance, ref state.SpinWait); - // Go around the loop one more time, keeping existing mutated state - WaitForWorkLoop(semaphore, state); - } - - private static void WorkLoopSemaphoreTimedOut(LowLevelLifoAsyncWaitSemaphore semaphore, object? stateObject) - { - SemaphoreWaitState state = (SemaphoreWaitState)stateObject!; - if (ShouldExitWorker(state.ThreadPoolInstance, state.ThreadAdjustmentLock)) { - // we're done, kill the thread. - - // we're wrapped in an emscripten eventloop handler which will consult the - // keepalive count, destroy the thread and run the TLS dtor which will - // unregister the thread from Mono - state.KeepaliveToken.Pop(); - return; - } else { - // more work showed up while we were shutting down, go around one more time - state.ResetIteration(); - WaitForWorkLoop(semaphore, state); - } - } - - private static void CreateWorkerThread() - { - // Thread pool threads must start in the default execution context without transferring the context, so - // using captureContext: false. - Thread workerThread = new Thread(s_workerThreadStart); - workerThread.IsThreadPoolThread = true; - workerThread.IsBackground = true; - // thread name will be set in thread proc - - // This thread will return to the JS event loop - tell the runtime not to cleanup - // after the start function returns, if the Emscripten keepalive is non-zero. - WebWorkerEventLoop.StartExitable(workerThread, captureContext: false); - } - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs index 406c5efcba46ec..78d45c30192ede 100644 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs @@ -14,7 +14,7 @@ namespace System.Threading { #if FEATURE_WASM_THREADS -#error when compiled with FEATURE_WASM_THREADS, we use PortableThreadPool.WorkerThread.Browser.Threads.Mono.cs +#error when compiled with FEATURE_WASM_THREADS, we use normal unix thread pool #endif [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public sealed class RegisteredWaitHandle : MarshalByRefObject diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs deleted file mode 100644 index 7933e49db422b9..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Threading -{ - public static partial class ThreadPool - { - // Indicates that the threadpool should yield the thread from the dispatch loop to the - // runtime periodically. We use this to return back to the JS event loop so that the JS - // event queue can be drained - internal static bool YieldFromDispatchLoop => true; - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs deleted file mode 100644 index 73c2959293d525..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/WebWorkerEventLoop.Browser.Threads.Mono.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; -using System.Runtime.CompilerServices; - -namespace System.Threading; - -/// -/// Keep a pthread alive in its WebWorker after its pthread start function returns. -/// -internal static class WebWorkerEventLoop -{ - // FIXME: these keepalive calls could be qcalls with a SuppressGCTransitionAttribute - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void KeepalivePushInternal(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void KeepalivePopInternal(); - - /// - /// A keepalive token prevents a thread from shutting down even if it returns to the JS event - /// loop. A thread may want a keepalive token if it needs to allow JS code to run to settle JS - /// promises or execute JS timeout callbacks. - /// - internal sealed class KeepaliveToken - { - public bool Valid {get; private set; } - - private KeepaliveToken() { Valid = true; } - - /// - /// Decrement the Emscripten keepalive count. A thread with a zero keepalive count will - /// terminate when it returns from its start function or from an async invocation from the - /// JS event loop. - /// - internal void Pop() { - if (!Valid) - throw new InvalidOperationException(); - Valid = false; - KeepalivePopInternal(); - } - - internal static KeepaliveToken Create() - { - KeepalivePushInternal(); - return new KeepaliveToken(); - } - } - - /// - /// Increment the Emscripten keepalive count. A thread with a positive keepalive can return from its - /// thread start function or a JS event loop invocation and continue running in the JS event - /// loop. - /// - internal static KeepaliveToken KeepalivePush() => KeepaliveToken.Create(); - - /// - /// Start a thread that may be kept alive on its webworker after the start function returns, - /// if the emscripten keepalive count is positive. Once the thread returns to the JS event - /// loop it will be able to settle JS promises as well as run any queued managed async - /// callbacks. - /// - internal static void StartExitable(Thread thread, bool captureContext) - { - // don't support captureContext == true, for now, since it's - // not needed by PortableThreadPool.WorkerThread - if (captureContext) - throw new InvalidOperationException(); - // for now, threadpool threads are exitable, and nothing else is. - if (!thread.IsThreadPoolThread) - throw new InvalidOperationException(); - thread.HasExternalEventLoop = true; - thread.UnsafeStart(); - } - - /// returns true if the current thread has unsettled JS Interop promises - private static bool HasUnsettledInteropPromises => HasUnsettledInteropPromisesNative(); - - // FIXME: this could be a qcall with a SuppressGCTransitionAttribute - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool HasUnsettledInteropPromisesNative(); - - /// returns true if the current WebWorker has JavaScript objects that depend on the - /// current managed thread. - /// - /// If this returns false, the runtime is allowed to allow the current managed thread - /// to exit and for the WebWorker to be recycled by Emscripten for another managed - /// thread. - internal static bool HasJavaScriptInteropDependents - { - // - // FIXME: - // https://github.com/dotnet/runtime/issues/85052 - unsettled promises are not the only relevant - // reasons for keeping a worker thread alive. We will need to add other conditions here. - get => HasUnsettledInteropPromises; - } -} diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index 704092c5a47c17..0e51652955423d 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -191,10 +191,6 @@ ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseIn #if (defined(HOST_BROWSER) && !defined(DISABLE_THREADS)) || (defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP)) ICALL_EXPORT gpointer ves_icall_System_Threading_LowLevelLifoAsyncWaitSemaphore_InitInternal (void); ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoAsyncWaitSemaphore_PrepareAsyncWaitInternal (gpointer sem_ptr, gint32 timeout_ms, gpointer success_cb, gpointer timeout_cb, intptr_t user_data); - -ICALL_EXPORT MonoBoolean ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void); -ICALL_EXPORT void ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void); -ICALL_EXPORT void ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void); #endif #ifdef TARGET_AMD64 diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index b18d285c421ca0..abd46d5a39ce82 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -610,14 +610,6 @@ HANDLES(THREAD_10, "SetState", ves_icall_System_Threading_Thread_SetState, void, HANDLES(THREAD_13, "StartInternal", ves_icall_System_Threading_Thread_StartInternal, void, 2, (MonoThreadObject, gint32)) NOHANDLES(ICALL(THREAD_14, "YieldInternal", ves_icall_System_Threading_Thread_YieldInternal)) -/* include these icalls if we're in the threaded wasm runtime, or if we're building a wasm-targeting cross compiler and we need to support --print-icall-table */ -#if (defined(HOST_BROWSER) && !defined(DISABLE_THREADS)) || (defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP)) -ICALL_TYPE(WEBWORKERLOOP, "System.Threading.WebWorkerEventLoop", WEBWORKERLOOP_1) -NOHANDLES(ICALL(WEBWORKERLOOP_1, "HasUnsettledInteropPromisesNative", ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative)) -NOHANDLES(ICALL(WEBWORKERLOOP_2, "KeepalivePopInternal", ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal)) -NOHANDLES(ICALL(WEBWORKERLOOP_3, "KeepalivePushInternal", ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal)) -#endif - ICALL_TYPE(TYPE, "System.Type", TYPE_1) HANDLES(TYPE_1, "internal_from_handle", ves_icall_System_Type_internal_from_handle, MonoReflectionType, 1, (MonoType_ref)) diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index dbed9f92e7f883..6f3621af9d051e 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -4958,26 +4958,6 @@ ves_icall_System_Threading_LowLevelLifoAsyncWaitSemaphore_PrepareAsyncWaitIntern mono_lifo_semaphore_asyncwait_prepare_wait (sem, timeout_ms, (LifoSemaphoreAsyncWaitCallbackFn)success_cb, (LifoSemaphoreAsyncWaitCallbackFn)timedout_cb, user_data); } -void -ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void) -{ - emscripten_runtime_keepalive_push(); -} - -void -ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void) -{ - emscripten_runtime_keepalive_pop(); -} - -extern int mono_wasm_eventloop_has_unsettled_interop_promises(void); - -MonoBoolean -ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void) -{ - return !!mono_wasm_eventloop_has_unsettled_interop_promises(); -} - #endif /* HOST_BROWSER && !DISABLE_THREADS */ /* for the AOT cross compiler with --print-icall-table these don't need to be callable, they just @@ -4995,22 +4975,5 @@ ves_icall_System_Threading_LowLevelLifoAsyncWaitSemaphore_PrepareAsyncWaitIntern g_assert_not_reached (); } -void -ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePushInternal (void) -{ - g_assert_not_reached(); -} - -void -ves_icall_System_Threading_WebWorkerEventLoop_KeepalivePopInternal (void) -{ - g_assert_not_reached(); -} - -MonoBoolean -ves_icall_System_Threading_WebWorkerEventLoop_HasUnsettledInteropPromisesNative(void) -{ - g_assert_not_reached(); -} #endif /* defined(TARGET_WASM) && defined(ENABLE_ICALL_SYMBOL_MAP) */ diff --git a/src/mono/sample/wasm/browser-threads-minimal/main.js b/src/mono/sample/wasm/browser-threads-minimal/main.js index 72631334586fb6..329a45aa99b429 100644 --- a/src/mono/sample/wasm/browser-threads-minimal/main.js +++ b/src/mono/sample/wasm/browser-threads-minimal/main.js @@ -12,9 +12,6 @@ try { const { getAssemblyExports, runMain } = await dotnet //.withEnvironmentVariable("MONO_LOG_LEVEL", "debug") //.withDiagnosticTracing(true) - .withConfig({ - pthreadPoolSize: 6, - }) .withElementOnExit() .withExitCodeLogging() .create();