diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
index f8e3712b5c3ae..3ec662c40afbc 100644
--- a/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
@@ -319,6 +319,8 @@ partial void ThreadNameChanged(string? value)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern DeserializationTracker GetThreadDeserializationTracker(ref StackCrawlMark stackMark);
+ internal const bool IsThreadStartSupported = true;
+
/// Returns true if the thread has been started and is not dead.
public extern bool IsAlive
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs
index f9c6a07f87309..dc8070ada491b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs
@@ -372,7 +372,7 @@ internal static void ResetThreadPoolThread(Thread currentThread)
[System.Diagnostics.Conditional("DEBUG")]
internal static void CheckThreadPoolAndContextsAreDefault()
{
- Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
+ Debug.Assert(!Thread.IsThreadStartSupported || Thread.CurrentThread.IsThreadPoolThread); // there are no dedicated threadpool threads on runtimes where we can't start threads
Debug.Assert(Thread.CurrentThread._executionContext == null, "ThreadPool thread not on Default ExecutionContext.");
Debug.Assert(Thread.CurrentThread._synchronizationContext == null, "ThreadPool thread not on Default SynchronizationContext.");
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs
index dd7b77f71f941..0472f6436171a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ThreadPoolTaskScheduler.cs
@@ -43,7 +43,7 @@ internal ThreadPoolTaskScheduler()
protected internal override void QueueTask(Task task)
{
TaskCreationOptions options = task.Options;
- if ((options & TaskCreationOptions.LongRunning) != 0)
+ if (Thread.IsThreadStartSupported && (options & TaskCreationOptions.LongRunning) != 0)
{
// Run LongRunning tasks on their own dedicated thread.
Thread thread = new Thread(s_longRunningThreadWork);
diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c
index 11efa13d3e0e3..3b914a03fddfe 100644
--- a/src/mono/mono/mini/mini-wasm.c
+++ b/src/mono/mono/mini/mini-wasm.c
@@ -395,6 +395,9 @@ extern void mono_set_timeout (int t, int d);
extern void mono_wasm_queue_tp_cb (void);
G_END_DECLS
+extern void mono_wasm_pump_threadpool (void);
+void mono_background_exec (void);
+
#endif // HOST_WASM
gpointer
@@ -620,12 +623,21 @@ mono_wasm_queue_tp_cb (void)
#endif
}
+void
+mono_wasm_pump_threadpool (void)
+{
+#ifdef HOST_WASM
+ mono_background_exec ();
+#endif
+}
+
void
mono_arch_register_icall (void)
{
#ifdef ENABLE_NETCORE
mono_add_internal_call_internal ("System.Threading.TimerQueue::SetTimeout", mono_wasm_set_timeout);
mono_add_internal_call_internal ("System.Threading.ThreadPool::QueueCallback", mono_wasm_queue_tp_cb);
+ mono_add_internal_call_internal ("System.Threading.ThreadPool::PumpThreadPool", mono_wasm_pump_threadpool);
#else
mono_add_internal_call_internal ("System.Threading.WasmRuntime::SetTimeout", mono_wasm_set_timeout);
#endif
diff --git a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
index 6c3acdd954b42..dac8da50c332a 100644
--- a/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -304,8 +304,12 @@
+
+
+
+
diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs
new file mode 100644
index 0000000000000..f34909158eb0c
--- /dev/null
+++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.Browser.Mono.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public partial class Thread
+ {
+ internal const bool IsThreadStartSupported = false;
+ }
+}
diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.UnixOrWindows.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.UnixOrWindows.Mono.cs
new file mode 100644
index 0000000000000..abd2a4425f4ee
--- /dev/null
+++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/Thread.UnixOrWindows.Mono.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public partial class Thread
+ {
+ internal const bool IsThreadStartSupported = true;
+ }
+}
diff --git a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
index 4b6636f6e34a1..77946c205110b 100644
--- a/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
+++ b/src/mono/netcore/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Mono.cs
@@ -107,9 +107,13 @@ private static RegisteredWaitHandle RegisterWaitForSingleObject(
}
[DynamicDependency("Callback")]
+ [DynamicDependency("PumpThreadPool")]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern void QueueCallback();
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern void PumpThreadPool(); // NOTE: this method is called via reflection by test code
+
private static void Callback()
{
_callbackQueued = false;