diff --git a/docs/workflow/building/libraries/webassembly-instructions.md b/docs/workflow/building/libraries/webassembly-instructions.md
index 474cbacf692c4f..e0752d846e31d7 100644
--- a/docs/workflow/building/libraries/webassembly-instructions.md
+++ b/docs/workflow/building/libraries/webassembly-instructions.md
@@ -113,6 +113,10 @@ The libraries build contains some native code. This includes shims over libc, op
./build.sh libs.native -os browser -c Debug|Release
```
+For multithreaded Browser builds, pass `/p:WasmEnableThreads=true` to both runtime and
+library builds. This enables `-pthread` for native library compilation and linking,
+including minipal. Do not mix these binaries with a single-threaded build.
+
## Building individual libraries
Individual projects and libraries can be build by specifying the build configuration.
diff --git a/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj b/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj
index 2b7a42c2f3842d..0864c7c3ea6b8a 100644
--- a/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj
+++ b/src/libraries/System.Threading.Thread/tests/System.Threading.Thread.Tests.csproj
@@ -21,4 +21,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Threading.Thread/tests/ThreadTests.cs b/src/libraries/System.Threading.Thread/tests/ThreadTests.cs
index 5160626bfa5d5e..8315a17266fbdc 100644
--- a/src/libraries/System.Threading.Thread/tests/ThreadTests.cs
+++ b/src/libraries/System.Threading.Thread/tests/ThreadTests.cs
@@ -32,6 +32,53 @@ public static class ThreadTests
private static object t_threadStaticReference;
private static int s_globalStaticValue;
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsMultithreadingSupported))]
+ [PlatformSpecific(TestPlatforms.AnyUnix | TestPlatforms.Browser)]
+ [InlineData(1)]
+ [InlineData(4)]
+ public static void NativeThreadIds_AreDistinctAndStable(int workerCount)
+ {
+ using var ready = new CountdownEvent(workerCount);
+ using var release = new ManualResetEventSlim();
+ ulong currentThreadId = Interop.Sys.GetCurrentThreadId();
+ ulong[] threadIds = new ulong[workerCount];
+ Action[] waitForThreads = new Action[workerCount];
+
+ for (int i = 0; i < workerCount; i++)
+ {
+ int index = i;
+ Thread thread = ThreadTestHelpers.CreateGuardedThread(out waitForThreads[i], () =>
+ {
+ threadIds[index] = Interop.Sys.GetCurrentThreadId();
+ ready.Signal();
+ Assert.True(release.Wait(UnexpectedTimeoutMilliseconds));
+ Assert.Equal(threadIds[index], Interop.Sys.GetCurrentThreadId());
+ });
+ thread.IsBackground = true;
+ thread.Start();
+ }
+
+ try
+ {
+ Assert.True(ready.Wait(UnexpectedTimeoutMilliseconds));
+ var uniqueIds = new HashSet { currentThreadId };
+ Assert.NotEqual(0UL, currentThreadId);
+ foreach (ulong threadId in threadIds)
+ {
+ Assert.NotEqual(0UL, threadId);
+ Assert.True(uniqueIds.Add(threadId), $"Duplicate native thread ID: {threadId}");
+ }
+ }
+ finally
+ {
+ release.Set();
+ foreach (Action waitForThread in waitForThreads)
+ {
+ waitForThread();
+ }
+ }
+ }
+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsMultithreadingSupported))]
public static void ConstructorTest()
{
diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj
index 7b2963b7948eae..6bddc2deea5f91 100644
--- a/src/libraries/tests.proj
+++ b/src/libraries/tests.proj
@@ -647,6 +647,7 @@
+
diff --git a/src/native/libs/CMakeLists.txt b/src/native/libs/CMakeLists.txt
index 2cd127c689c9f3..e3532829d6a5ec 100644
--- a/src/native/libs/CMakeLists.txt
+++ b/src/native/libs/CMakeLists.txt
@@ -9,6 +9,11 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
include(../../../eng/native/configurepaths.cmake)
include(${CLR_ENG_NATIVE_DIR}/configurecompiler.cmake)
+ if (CLR_CMAKE_TARGET_BROWSER AND CLR_CMAKE_WASM_ENABLE_THREADS)
+ add_compile_options(-pthread)
+ add_link_options(-pthread)
+ endif ()
+
add_subdirectory(${CLR_SRC_NATIVE_DIR}/minipal minipal)
endif()