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()