diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake
index b4aaa7fedbf5..d99e6c4bacf5 100644
--- a/eng/native/configurecompiler.cmake
+++ b/eng/native/configurecompiler.cmake
@@ -148,6 +148,10 @@ elseif (CLR_CMAKE_HOST_UNIX)
add_compile_options(-Wno-alloca)
add_compile_options(-Wno-implicit-int-float-conversion)
endif()
+
+ if (CLR_CMAKE_TARGET_OS_SUBGROUP STREQUAL multithread AND CLR_CMAKE_HOST_BROWSER)
+ add_compile_options(-pthread)
+ endif(CLR_CMAKE_TARGET_OS_SUBGROUP STREQUAL multithread AND CLR_CMAKE_HOST_BROWSER)
endif(MSVC)
if (CLR_CMAKE_ENABLE_SANITIZERS)
diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd
index b78a073c8c11..8d2d75f4e99f 100644
--- a/eng/native/gen-buildsys.cmd
+++ b/eng/native/gen-buildsys.cmd
@@ -65,7 +65,7 @@ if /i "%__Arch%" == "wasm" (
set "WASI_SDK_PATH=!WASI_SDK_PATH:\=/!"
if not "!WASI_SDK_PATH:~-1!" == "/" set "WASI_SDK_PATH=!WASI_SDK_PATH!/"
set __CmakeGenerator=Ninja
- set __ExtraCmakeParams=%__ExtraCmakeParams% -DCLR_CMAKE_TARGET_OS=wasi -DCLR_CMAKE_TARGET_ARCH=wasm "-DWASI_SDK_PREFIX=!WASI_SDK_PATH!" "-DCMAKE_TOOLCHAIN_FILE=!WASI_SDK_PATH!/share/cmake/wasi-sdk-p2.cmake" "-DCMAKE_SYSROOT=!WASI_SDK_PATH!share/wasi-sysroot" "-DCMAKE_CROSSCOMPILING_EMULATOR=node --experimental-wasm-bigint --experimental-wasi-unstable-preview1"
+ set __ExtraCmakeParams=%__ExtraCmakeParams% -DCLR_CMAKE_TARGET_OS=wasi -DCLR_CMAKE_TARGET_ARCH=wasm "-DWASI_SDK_PREFIX=!WASI_SDK_PATH!" "-DCMAKE_TOOLCHAIN_FILE=!WASI_SDK_PATH!/share/cmake/wasi-sdk-p2.cmake" "-DCMAKE_SYSROOT=!WASI_SDK_PATH!share/wasi-sysroot" "-DCMAKE_CROSSCOMPILING_EMULATOR=node --experimental-wasm-bigint --experimental-wasi-unstable-preview1 -D_WASI_EMULATED_PTHREAD"
)
) else (
set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_SYSTEM_VERSION=10.0"
diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml
index abe13f65b536..161efb15b2c2 100644
--- a/eng/pipelines/common/global-build-job.yml
+++ b/eng/pipelines/common/global-build-job.yml
@@ -219,7 +219,7 @@ jobs:
- script: pwsh $(Build.SourcesDirectory)/eng/pipelines/runtimelab/install-nodejs.ps1 $(Build.SourcesDirectory)/wasm-tools
displayName: Install NodeJS
- - ${{ if and(eq(parameters.runtimeFlavor, 'coreclr'), or(eq(parameters.platform, 'wasi_wasm_win'), eq(parameters.platform, 'wasi_wasm_linux_x64_naot_llvm'))) }}:
+ - ${{ if and(eq(parameters.runtimeFlavor, 'coreclr'), in(parameters.platform, 'wasi_wasm_win', 'wasi_wasm_linux_x64_naot_llvm', 'wasi_multithread_wasm_win')) }}:
# Install Wasi Wasm dependencies: wasi-sdk, wasmtime
- script: pwsh $(Build.SourcesDirectory)/eng/pipelines/runtimelab/install-wasi-sdk.ps1 -CI -InstallDir $(Build.SourcesDirectory)/wasm-tools
displayName: Install wasi-sdk
@@ -228,7 +228,7 @@ jobs:
- script: pwsh $(Build.SourcesDirectory)/eng/pipelines/runtimelab/install-jco.ps1 $(Build.SourcesDirectory)
displayName: Install Jco
- - ${{ if or(eq(parameters.platform, 'browser_wasm_win'), and(eq(parameters.platform, 'wasi_wasm_win'), not(eq(parameters.runtimeFlavor, 'coreclr')))) }}:
+ - ${{ if or(eq(parameters.platform, 'browser_wasm_win'), and(in(parameters.platform, 'wasi_wasm_win', 'wasi_multithread_wasm_win'), not(eq(parameters.runtimeFlavor, 'coreclr')))) }}:
# Update machine certs
- task: PowerShell@2
displayName: Update machine certs
diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml
index 1c34c26eecdb..d427599f2e33 100644
--- a/eng/pipelines/common/platform-matrix.yml
+++ b/eng/pipelines/common/platform-matrix.yml
@@ -500,6 +500,44 @@ jobs:
buildConfig: ${{ parameters.buildConfig }}
${{ insert }}: ${{ parameters.jobParameters }}
+- ${{ if containsValue(parameters.platforms, 'wasi_multithread_wasm_win') }}:
+ - template: xplat-setup.yml
+ parameters:
+ jobTemplate: ${{ parameters.jobTemplate }}
+ helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }}
+ variables: ${{ parameters.variables }}
+ osGroup: wasi
+ osSubgroup: multithread
+ archType: wasm
+ targetRid: wasi-wasm
+ platform: wasi_multithread_wasm_win
+ shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+ jobParameters:
+ hostedOs: windows
+ runtimeFlavor: ${{ parameters.runtimeFlavor }}
+ stagedBuild: ${{ parameters.stagedBuild }}
+ buildConfig: ${{ parameters.buildConfig }}
+ ${{ insert }}: ${{ parameters.jobParameters }}
+
+- ${{ if containsValue(parameters.platforms, 'browser_multithread_wasm_win') }}:
+ - template: xplat-setup.yml
+ parameters:
+ jobTemplate: ${{ parameters.jobTemplate }}
+ helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }}
+ variables: ${{ parameters.variables }}
+ osGroup: browser
+ osSubgroup: multithread
+ archType: wasm
+ targetRid: browser-wasm
+ platform: browser_multithread_wasm_win
+ shouldContinueOnError: ${{ parameters.shouldContinueOnError }}
+ jobParameters:
+ hostedOs: windows
+ runtimeFlavor: ${{ parameters.runtimeFlavor }}
+ stagedBuild: ${{ parameters.stagedBuild }}
+ buildConfig: ${{ parameters.buildConfig }}
+ ${{ insert }}: ${{ parameters.jobParameters }}
+
# Browser WebAssembly Linux X64 for NAOT-LLVM
# Use a different name to differentiate from upstream as we need the -sanitizer image to get build tools that are not present in the stock browser_wasm image.
diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml
index 46f897e80b25..49c46621806b 100644
--- a/eng/pipelines/libraries/helix-queues-setup.yml
+++ b/eng/pipelines/libraries/helix-queues-setup.yml
@@ -175,7 +175,7 @@ jobs:
- (Ubuntu.2204.Amd64)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-webassembly
# Browser WebAssembly windows
- - ${{ if in(parameters.platform, 'browser_wasm_win', 'wasi_wasm_win') }}:
+ - ${{ if in(parameters.platform, 'browser_wasm_win', 'wasi_wasm_win', 'browser_multithread_wasm_win', 'wasi_multithread_wasm_win') }}:
- (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly
- (Windows.Server2025.Amd64.Open)windows.server2025.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2025-helix-webassembly-amd64
diff --git a/eng/pipelines/runtimelab.yml b/eng/pipelines/runtimelab.yml
index cf88871b348c..e07909b023a9 100644
--- a/eng/pipelines/runtimelab.yml
+++ b/eng/pipelines/runtimelab.yml
@@ -71,6 +71,25 @@ extends:
parameters:
librariesConfiguration: Debug
+ #
+ # Build and test Wasm Debug multithreaded libraries and Debug runtime
+ #
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml
+ buildConfig: debug
+ platforms:
+ - browser_multithread_wasm_win
+ - wasi_multithread_wasm_win
+ jobParameters:
+ timeoutInMinutes: 300
+ buildArgs: -s clr.aot+libs -c debug -rc $(_BuildConfig) -cmakeargs -DCLR_CMAKE_TARGET_OS_SUBGROUP=multithread '/p:WasmEnableThreads=true'
+ postBuildSteps:
+ - template: /eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
+ parameters:
+ librariesConfiguration: Debug
+
#
# Build and test with Debug libraries and Checked runtime
#
diff --git a/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml b/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
index fd00f4681ddd..93532453c7cf 100644
--- a/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
+++ b/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
@@ -37,6 +37,14 @@ steps:
- script: |
$(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.archType }} $(crossArg) $(_officialBuildParameter) ci wasi tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }}
displayName: Build WebAssembly tests
+ - ${{ elseif eq(parameters.platform, 'browser_multithread_wasm_win') }}:
+ - script: |
+ $(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.archType }} $(crossArg) $(_officialBuildParameter) ci browser tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }} /p:FeatureWasmManagedThreads=true /p:WasmEnableThreads=true
+ displayName: Build WebAssembly browser Mutlithread tests
+ - ${{ elseif eq(parameters.platform, 'wasi_multithread_wasm_win') }}:
+ - script: |
+ $(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.archType }} $(crossArg) $(_officialBuildParameter) ci wasi tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }} /p:FeatureWasmManagedThreads=true
+ displayName: Build WebAssembly wasi Mutlithread tests
- ${{ elseif eq(parameters.platform, 'Browser_wasm_linux_x64_naot_llvm') }}:
- script: |
source $(Build.SourcesDirectory)/wasm-tools/emsdk/emsdk_env.sh
@@ -59,6 +67,9 @@ steps:
call $(Build.SourcesDirectory)\wasm-tools\emsdk\emsdk_env
$(Build.SourcesDirectory)/src/tests/run$(scriptExt) runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
displayName: Run WebAssembly tests in single file mode
+ - ${{ elseif in(parameters.platform, 'browser_multithread_wasm_win', 'wasi_multithread_wasm_win') }}:
+ - script: $(Build.SourcesDirectory)/src/tests/run$(scriptExt) runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
+ displayName: Run WebAssembly multithread tests in single file mode
- ${{ elseif eq(parameters.osGroup, 'windows') }}:
- script: $(Build.SourcesDirectory)/src/tests/run$(scriptExt) runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
displayName: Run tests in single file mode
@@ -67,9 +78,14 @@ steps:
displayName: Run tests in single file mode
- ${{ if eq(parameters.archType, 'wasm') }}:
- - script: |
+ - ${{ if in(parameters.platform, 'browser_multithread_wasm_win', 'wasi_multithread_wasm_win') }}:
+ - script: |
+ $(Build.SourcesDirectory)/build$(scriptExt) libs.tests -test -a ${{ parameters.archType }} -os ${{ parameters.osGroup }} -lc ${{ parameters.librariesConfiguration }} -rc $(buildConfigUpper) /p:TestNativeAot=true /p:RunSmokeTestsOnly=true /p:FeatureWasmManagedThreads=true /p:WasmEnableThreads=true
+ displayName: Build and run WebAssembly libraries tests
+ - ${{ else }}:
+ - script: |
$(Build.SourcesDirectory)/build$(scriptExt) libs.tests -test -a ${{ parameters.archType }} -os ${{ parameters.osGroup }} -lc ${{ parameters.librariesConfiguration }} -rc $(buildConfigUpper) /p:TestNativeAot=true /p:RunSmokeTestsOnly=true
- displayName: Build and run WebAssembly libraries tests
+ displayName: Build and run WebAssembly libraries tests
# Upload unsigned artifacts
- ${{ if eq(parameters.uploadIntermediateArtifacts, true) }}:
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index 91bf830300bb..c49c231b6c3f 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -438,6 +438,7 @@ The .NET Foundation licenses this file to you under the MIT license.
$(CompileWasmArgs) -g3
$(CompileWasmArgs) -mnontrapping-fptoint
$(CompileWasmArgs) -fwasm-exceptions
+ $(CompileWasmArgs) -pthread -matomics -mbulk-memory
@@ -608,6 +609,7 @@ The .NET Foundation licenses this file to you under the MIT license.
+
diff --git a/src/coreclr/nativeaot/CMakeLists.txt b/src/coreclr/nativeaot/CMakeLists.txt
index aff6c47180c9..babf87150dde 100644
--- a/src/coreclr/nativeaot/CMakeLists.txt
+++ b/src/coreclr/nativeaot/CMakeLists.txt
@@ -32,6 +32,12 @@ if(CLR_CMAKE_TARGET_OS STREQUAL wasi)
add_definitions(-DTARGET_UNIX)
endif(CLR_CMAKE_TARGET_OS STREQUAL wasi)
+if((CLR_CMAKE_TARGET_OS STREQUAL wasi OR CLR_CMAKE_TARGET_OS STREQUAL emscripten)
+ AND CLR_CMAKE_TARGET_OS_SUBGROUP STREQUAL multithread)
+ add_definitions(-DFEATURE_WASM_MANAGED_THREADS)
+endif((CLR_CMAKE_TARGET_OS STREQUAL wasi OR CLR_CMAKE_TARGET_OS STREQUAL emscripten)
+ AND CLR_CMAKE_TARGET_OS_SUBGROUP STREQUAL multithread)
+
if(CLR_CMAKE_TARGET_ANDROID)
add_definitions(-DFEATURE_EMULATED_TLS)
endif()
diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
index 5456ed029459..3448494fbce8 100644
--- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
+++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
@@ -1151,6 +1151,8 @@ REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out
{
#if defined(HOST_WASM) && !defined(FEATURE_WASM_MANAGED_THREADS)
PalGetMaximumStackBounds_SingleThreadedWasm(&pStackLowOut, &pStackHighOut);
+#elif defined(HOST_WASM) && defined(FEATURE_WASM_MANAGED_THREADS)
+ PalGetMaximumStackBounds_MultiThreadedWasm(&pStackLowOut, &pStackHighOut);
#elif defined(__APPLE__)
// This is a Mac specific method
pStackHighOut = pthread_get_stackaddr_np(pthread_self());
diff --git a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
index a210ddaa4245..efcc1402ad36 100644
--- a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
+++ b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
@@ -191,6 +191,184 @@ extern "C" int __cxa_thread_atexit(Dtor dtor, void* obj, void*)
#endif // TARGET_WASI
#endif // !FEATURE_WASM_MANAGED_THREADS
+// TODO-LLVM: For now, a copy of the single threaded implementation.
+#ifdef FEATURE_WASM_MANAGED_THREADS
+int __cxa_thread_atexit(void (*func)(), void *obj, void *dso_symbol)
+{
+ return 0;
+}
+
+//
+// Note that we return the native stack bounds here, not shadow stack ones. Currently this functionality is mainly
+// used for RuntimeHelpers.TryEnsureSufficientExecutionStack, and we do use the native stack in codegen, so this
+// is an acceptable approximation.
+//
+extern "C" unsigned char __stack_low;
+extern "C" unsigned char __stack_high;
+void PalGetMaximumStackBounds_MultiThreadedWasm(void** ppStackLowOut, void** ppStackHighOut)
+{
+ // See https://github.com/emscripten-core/emscripten/pull/18057 and https://reviews.llvm.org/D135910.
+ unsigned char* pStackLow = &__stack_low;
+ unsigned char* pStackHigh = &__stack_high;
+
+ // Sanity check that we have the expected memory layout.
+ ASSERT((pStackHigh - pStackLow) >= 64 * 1024);
+ if (pStackLow >= pStackHigh)
+ {
+ PalPrintFatalError("\nFatal error. Unexpected stack layout.\n");
+ RhFailFast();
+ }
+
+ *ppStackLowOut = pStackLow;
+ *ppStackHighOut = pStackHigh;
+}
+
+#ifdef TARGET_WASI
+// No-op stubs that assume a single-threaded environment.
+int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *)
+{
+ return 0;
+}
+
+int pthread_mutexattr_init(pthread_mutexattr_t *)
+{
+ return 0;
+}
+
+int pthread_mutexattr_settype(pthread_mutexattr_t *, int)
+{
+ return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *)
+{
+ return 0;
+}
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t *)
+{
+ return 0;
+}
+
+int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *)
+{
+ return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *)
+{
+ return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *)
+{
+ return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *)
+{
+ return 0;
+}
+
+int pthread_condattr_init(pthread_condattr_t *)
+{
+ return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *)
+{
+ return 0;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *)
+{
+ return 0;
+}
+
+pthread_t pthread_self(void)
+{
+ return (pthread_t)0;
+}
+
+int pthread_equal(pthread_t, pthread_t)
+{
+ return 1; // only one thread
+}
+
+int pthread_attr_init(pthread_attr_t *)
+{
+ return 0;
+}
+
+int pthread_attr_destroy(pthread_attr_t *)
+{
+ return 0;
+}
+
+int pthread_condattr_destroy(pthread_condattr_t *)
+{
+ return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t *)
+{
+ return 0;
+}
+
+int pthread_attr_setdetachstate(pthread_attr_t *, int)
+{
+ return 0;
+}
+
+using Dtor = void(*)(void*);
+
+// Due to a bug in the toolchain, we have to provide an implementation of thread-local destruction.
+// Since this is the single-threaded case, we simply delegate to the static destruction mechanism.
+// Reference: https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_thread_atexit.cpp.
+//
+extern "C" int __cxa_thread_atexit(Dtor dtor, void* obj, void*)
+{
+ struct DtorList
+ {
+ Dtor dtor;
+ void* obj;
+ DtorList* next;
+ };
+
+ struct DtorsManager
+ {
+ DtorList* m_dtors = nullptr;
+
+ ~DtorsManager()
+ {
+ while (DtorList* head = m_dtors)
+ {
+ m_dtors = head->next;
+ head->dtor(head->obj);
+ free(head);
+ }
+ }
+ };
+
+ // The linked list of "thread-local" destructors to run.
+ static DtorsManager s_dtorsManager;
+
+ DtorList* head = static_cast(malloc(sizeof(DtorList)));
+ if (head == nullptr)
+ {
+ return -1;
+ }
+
+ head->dtor = dtor;
+ head->obj = obj;
+ head->next = s_dtorsManager.m_dtors;
+ s_dtorsManager.m_dtors = head;
+
+ return 0;
+}
+#endif // TARGET_WASI
+#endif // FEATURE_WASM_MANAGED_THREADS
+
// Recall that WASM's model is extremely simple: we have one linear memory, which can only be grown, in chunks
// of 64K pages. Thus, "mmap"/"munmap" fundamentally cannot be faithfully recreated and the Unix emulators we
// layer on top of (Emscripten/WASI libc) reflect this by not supporting the scenario. Fortunately, the current
diff --git a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h
index 3030a582b58a..ff75454e6aba 100644
--- a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h
+++ b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#ifndef FEATURE_WASM_MANAGED_THREADS
+#ifdef FEATURE_WASM_MANAGED_THREADS
+int __cxa_thread_atexit(void (*func)(), void *obj, void *dso_symbol);
+void PalGetMaximumStackBounds_MultiThreadedWasm(void** ppStackLowOut, void** ppStackHighOut);
+#else
void PalGetMaximumStackBounds_SingleThreadedWasm(void** ppStackLowOut, void** ppStackHighOut);
#endif // !FEATURE_WASM_MANAGED_THREADS
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 7f09dfcc7608..b758b1618151 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -35,6 +35,10 @@
true
true
+ true
+ true
+ $(DefineConstants);FEATURE_WASM_MANAGED_THREADS
+ $(DefineConstants);FEATURE_WASM_PERFTRACING
true
@@ -347,6 +351,8 @@
+
+
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs
index 4c90bbaa42ad..9c33774f1619 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs
@@ -143,7 +143,9 @@ public static bool IsEntered(object obj)
#region Public Wait/Pulse methods
+#if !FEATURE_WASM_MANAGED_THREADS
[UnsupportedOSPlatform("browser")]
+#endif
public static bool Wait(object obj, int millisecondsTimeout)
{
return GetCondition(obj).Wait(millisecondsTimeout, obj);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PortableThreadPool.NativeAot.Browser.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PortableThreadPool.NativeAot.Browser.cs
new file mode 100644
index 000000000000..cc3f606fe627
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/PortableThreadPool.NativeAot.Browser.cs
@@ -0,0 +1,19 @@
+// 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;
+
+internal sealed partial class PortableThreadPool
+{
+ private static partial class WorkerThread
+ {
+ private static bool IsIOPending => false;
+ }
+
+ private struct CpuUtilizationReader
+ {
+#pragma warning disable CA1822
+ public double CurrentUtilization => 0.0; // FIXME: can we do better
+#pragma warning restore CA1822
+ }
+}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.Browser.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.Browser.cs
new file mode 100644
index 000000000000..ca2656955006
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/ThreadPool.NativeAot.Browser.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.
+
+namespace System.Threading;
+
+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 => true;
+}
diff --git a/src/installer/pkg/projects/nativeaot-packages.proj b/src/installer/pkg/projects/nativeaot-packages.proj
index 8a640a9c08c0..0a1404df62c1 100644
--- a/src/installer/pkg/projects/nativeaot-packages.proj
+++ b/src/installer/pkg/projects/nativeaot-packages.proj
@@ -4,7 +4,8 @@
-
+
+
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.NativeAOT.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.NativeAOT.cs
index 3d2fdaae538d..cb1f96ea465b 100644
--- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.NativeAOT.cs
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.NativeAOT.cs
@@ -28,6 +28,27 @@ internal static unsafe partial class Runtime
[LibraryImport(JSLibrary, EntryPoint = "mono_wasm_cancel_promise", StringMarshalling = StringMarshalling.Utf16)]
public static unsafe partial void CancelPromise(IntPtr gcHandle);
+#if FEATURE_WASM_MANAGED_THREADS
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_invoke_js_function_send")]
+ public static unsafe partial void InvokeJSFunctionSend(nint targetNativeTID, nint functionHandle, nint data);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_install_js_worker_interop_wrapper")]
+ public static unsafe partial void InstallWebWorkerInterop(nint proxyContextGCHandle, void* beforeSyncJSImport, void* afterSyncJSImport, void* pumpHandler);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_cancel_promise_post")]
+ public static unsafe partial void CancelPromisePost(nint targetNativeTID, nint taskHolderGCHandle);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_invoke_jsimport_MT")]
+ public static unsafe partial void InvokeJSImportSync(nint signature, nint args);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_invoke_jsimport_sync_send")]
+ public static unsafe partial void InvokeJSImportSyncSend(nint targetNativeTID, nint signature, nint args);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_invoke_jsimport_async_post")]
+ public static unsafe partial void InvokeJSImportAsyncPost(nint targetNativeTID, nint signature, nint args);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_resolve_or_reject_promise_post")]
+ public static unsafe partial void ResolveOrRejectPromisePost(nint targetNativeTID, nint data);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_release_cs_owned_object_post")]
+ internal static unsafe partial void ReleaseCSOwnedObjectPost(nint targetNativeTID, nint jsHandle);
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_uninstall_js_worker_interop")]
+ public static unsafe partial void UninstallWebWorkerInterop();
+#endif
+
#region Not used by NativeAOT
public static IntPtr RegisterGCRoot(void* start, int bytesSize, IntPtr name) => throw new NotImplementedException();
public static void DeregisterGCRoot(IntPtr handle) => throw new NotImplementedException();
diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
index f26a1dbf1bdc..42b47e6e78e7 100644
--- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
+++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj
@@ -76,6 +76,11 @@
+
+
+
+
+
diff --git a/src/mono/browser/runtime/loader/polyfills.ts b/src/mono/browser/runtime/loader/polyfills.ts
index 181323b0fe0a..382d6da00d53 100644
--- a/src/mono/browser/runtime/loader/polyfills.ts
+++ b/src/mono/browser/runtime/loader/polyfills.ts
@@ -22,7 +22,7 @@ export function verifyEnvironment () {
mono_assert(ENVIRONMENT_IS_SHELL || typeof globalThis.URL === "function", "This browser/engine doesn't support URL API. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
mono_assert(typeof globalThis.BigInt64Array === "function", "This browser/engine doesn't support BigInt64Array API. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
if (WasmEnableThreads) {
- mono_assert(!ENVIRONMENT_IS_SHELL && !ENVIRONMENT_IS_NODE, "This build of dotnet is multi-threaded, it doesn't support shell environments like V8 or NodeJS. See also https://aka.ms/dotnet-wasm-features");
+ //mono_assert(!ENVIRONMENT_IS_SHELL && !ENVIRONMENT_IS_NODE, "This build of dotnet is multi-threaded, it doesn't support shell environments like V8 or NodeJS. See also https://aka.ms/dotnet-wasm-features");
mono_assert(globalThis.SharedArrayBuffer !== undefined, "SharedArrayBuffer is not enabled on this page. Please use a modern browser and set Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy http headers. See also https://aka.ms/dotnet-wasm-features");
mono_assert(typeof globalThis.EventTarget === "function", "This browser/engine doesn't support EventTarget API. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
diff --git a/src/native/external/zlib-ng.cmake b/src/native/external/zlib-ng.cmake
index b2338d682e14..99f6d6ba4f9b 100644
--- a/src/native/external/zlib-ng.cmake
+++ b/src/native/external/zlib-ng.cmake
@@ -26,8 +26,13 @@ if (CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI)
# zlib-ng uses atomics, so we need to enable threads when requested for browser/wasi, otherwise the wasm target won't have thread support.
if (CMAKE_USE_PTHREADS)
+ if (CLR_CMAKE_TARGET_BROWSER)
add_compile_options(-pthread)
add_linker_flag(-pthread)
+ else()
+ add_compile_options(-D_WASI_EMULATED_PTHREAD )
+ add_linker_flag(-Wl,-lwasi-emulated-pthread)
+ endif()
endif()
endif()
diff --git a/src/tests/Common/dirs.proj b/src/tests/Common/dirs.proj
index 6e1283eeb4e9..c78b43641a6f 100644
--- a/src/tests/Common/dirs.proj
+++ b/src/tests/Common/dirs.proj
@@ -29,6 +29,13 @@
+
+
+
+
+
+
+
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 66652353a200..990150bdb5f0 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -490,6 +490,8 @@
$(GroupBuildCmd) "/p:MonoForceInterpreter=true"
$(GroupBuildCmd) "/p:RunAOTCompilation=true"
$(GroupBuildCmd) "/p:DevTeamProvisioning=$(DevTeamProvisioning)"
+ $(GroupBuildCmd) "/p:FeatureWasmManagedThreads=$(FeatureWasmManagedThreads)"
+ $(GroupBuildCmd) "/p:WasmEnableThreads=$(WasmEnableThreads)"
diff --git a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs
index 0d8dd70eccf4..2c856609e13e 100644
--- a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs
+++ b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.cs
@@ -40,7 +40,9 @@ private static unsafe int Main(string[] args)
TestMetaData();
TestGC();
+#if !NO_EXPLICIT_FINALIZER
TestFinalization();
+#endif
Add(1, 2);
PrintLine("Hello from C#!");
diff --git a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.csproj b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.csproj
index df4345c5e749..ee681a5b67f3 100644
--- a/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.csproj
+++ b/src/tests/nativeaot/SmokeTests/HelloWasm/HelloWasm.csproj
@@ -10,6 +10,7 @@
true
false
true
+ $(DefineConstants);NO_EXPLICIT_FINALIZER