diff --git a/docs/workflow/building/coreclr/nativeaot.md b/docs/workflow/building/coreclr/nativeaot.md
index bb3d2817da0c..8642acef81a4 100644
--- a/docs/workflow/building/coreclr/nativeaot.md
+++ b/docs/workflow/building/coreclr/nativeaot.md
@@ -76,6 +76,26 @@ You should now be able to publish the project for Wasm: `dotnet publish -r brows
Once you build the repo, you can use the produced binaries in one of four ways specified below ("Using built binaries", "Building packages", "Convenience Visual Studio "repro" project", "Running tests").
+## Building for Multithreaded packages
+
+This is a work in progress and far from functional. Currentlty there exists just enough infrastucture to build packages for mutlithreaded runtime and libs, but they are not functional in the sense that they support multithreaded programs yet. To build the WASI packages:
+```
+build clr.aot+libs+nativeaot.packages -c Debug -a wasm -os wasi '/p:WasmEnableThreads=true'
+```
+To build the browser multithreaded packages:
+```
+build clr.aot+libs+nativeaot.packages -c Debug -a wasm -os browser '/p:WasmEnableThreads=true'
+```
+To build the runtime tests for WASI
+```
+src\tests\build nativeaot Debug wasm tree nativeaot wasi /p:LibrariesConfiguration=debug /p:TestWrapperTargetsWindows=true /p:WasmEnableThreads=true
+```
+To build the runtime tests for browser
+```
+src\tests\build nativeaot Debug wasm tree nativeaot browser /p:LibrariesConfiguration=debug /p:TestWrapperTargetsWindows=true /p:WasmEnableThreads=true
+```
+
+
### Using built binaries
In this workflow, you have a project file that you want to `dotnet publish`, but you want to use your own build of the compiler/runtime/framework. You need to be using a daily build of the .NET SDK downloaded from the dotnet/sdk repo. It's typically enough to just download the daily build ZIP file, unpack it, and make sure the unpacked directory is the first thing in your PATH. Don't forget to add a NuGet.config as specified by the dotnet/sdk repo- you'll hit restore issues otherwise.
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/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml
index c0d705a4cf6f..968f92e7a5b1 100644
--- a/eng/pipelines/common/platform-matrix.yml
+++ b/eng/pipelines/common/platform-matrix.yml
@@ -489,6 +489,7 @@ jobs:
helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }}
variables: ${{ parameters.variables }}
osGroup: wasi
+ osSubGroup: ${{ parameters.osSubGroup }}
archType: wasm
targetRid: wasi-wasm
platform: wasi_wasm_win
@@ -611,6 +612,7 @@ jobs:
helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }}
variables: ${{ parameters.variables }}
osGroup: browser
+ osSubGroup: ${{ parameters.osSubGroup }}
archType: wasm
targetRid: browser-wasm
platform: browser_wasm_win
diff --git a/eng/pipelines/runtimelab.yml b/eng/pipelines/runtimelab.yml
index 29e389806642..e74e189e3deb 100644
--- a/eng/pipelines/runtimelab.yml
+++ b/eng/pipelines/runtimelab.yml
@@ -71,6 +71,28 @@ 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
+ osSubgroup: multithread
+ platforms:
+ - browser_wasm_win
+ - wasi_wasm_win
+ jobParameters:
+ timeoutInMinutes: 300
+ buildArgs: -s clr.aot+libs -c debug -rc $(_BuildConfig) '/p:WasmEnableThreads=true'
+ nameSuffix: Multithreaded
+ postBuildSteps:
+ - template: /eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
+ parameters:
+ librariesConfiguration: Debug
+ wasmEnableThreadsArg: /p:WasmEnableThreads=true
+
#
# 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 24c039f8865d..7c4e635585b8 100644
--- a/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
+++ b/eng/pipelines/runtimelab/runtimelab-post-build-steps.yml
@@ -8,6 +8,7 @@ parameters:
runtimeVariant: ''
isOfficialBuild: false
librariesConfiguration: Debug
+ wasmEnableThreadsArg: ''
steps:
# For NativeAOT-LLVM, we have just built the Wasm-targeting native artifacts (the runtime and libraries).
@@ -17,7 +18,7 @@ steps:
displayName: Build the ILC and RyuJit cross-compilers
# Build target packages (note: target libs already built).
- - script: $(Build.SourcesDirectory)/build$(scriptExt) nativeaot.packages -os ${{ parameters.osGroup }} -a wasm -c $(buildConfigUpper) $(_officialBuildParameter) -ci
+ - script: $(Build.SourcesDirectory)/build$(scriptExt) nativeaot.packages -os ${{ parameters.osGroup }} -a wasm -c $(buildConfigUpper) $(_officialBuildParameter) -ci $(wasmEnableThreadsArg)
displayName: Build target packages
# Build host packages.
@@ -26,13 +27,14 @@ steps:
# Build coreclr native test output outside of official build
- ${{ if ne(parameters.isOfficialBuild, true) }}:
- - ${{ if and(eq(parameters.archType, 'wasm'), ne(parameters.nameSuffix, '')) }}:
+ - ${{ if and(eq(parameters.archType, 'wasm'), and(ne(parameters.nameSuffix, ''), eq(parameters.wasmEnableThreadsArg, ''))) }}:
- script: pwsh $(Build.SourcesDirectory)/eng/pipelines/runtimelab/set-ilc-emulation-environment.ps1 -Arch $(hostedTargetArch)
displayName: Set up ILC emulation environment
- ${{ if eq(parameters.archType, 'wasm') }}:
- - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.osGroup }} $(crossArg) $(_officialBuildParameter) ci tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }}
- displayName: Build runtime tests
+ - ${{ if not(and(eq(parameters.osGroup, 'browser'), ne(parameters.wasmEnableThreadsArg, ''))) }}:
+ - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.osGroup }} $(crossArg) $(_officialBuildParameter) ci tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }} ${{ parameters.wasmEnableThreadsArg }}
+ displayName: Build runtime tests
- ${{ else }}:
- ${{ if eq(parameters.osGroup, 'windows') }}:
- script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) nativeaot $(buildConfigUpper) ${{ parameters.archType }} $(crossArg) $(_officialBuildParameter) ci tree nativeaot /p:LibrariesConfiguration=${{ parameters.librariesConfiguration }}
@@ -45,11 +47,12 @@ steps:
- script: $(Build.SourcesDirectory)/src/tests/run$(scriptExt) runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
displayName: Run runtime tests
- ${{ else }}:
- - script: $(Build.SourcesDirectory)/src/tests/run$(scriptExt) --runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
- displayName: Run runtime tests
+ - ${{ if not(and(eq(parameters.osGroup, 'browser'), ne(parameters.wasmEnableThreadsArg, ''))) }}:
+ - script: $(Build.SourcesDirectory)/src/tests/run$(scriptExt) --runnativeaottests $(buildConfigUpper) ${{ parameters.archType }} ${{ parameters.osGroup }}
+ displayName: Run runtime tests
# Don't compile/run the libraries tests with emulated ILC to save CI time/resources.
- - ${{ if and(eq(parameters.archType, 'wasm'), eq(parameters.nameSuffix, '')) }}:
+ - ${{ if and(eq(parameters.archType, 'wasm'), eq(parameters.wasmEnableThreadsArg, '')) }}:
- 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
@@ -61,3 +64,4 @@ steps:
- template: /eng/pipelines/common/upload-intermediate-artifacts-step.yml
parameters:
name: ${{ parameters.platform }}${{ parameters.nameSuffix }}
+
\ No newline at end of file
diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index d1ea61f025f9..bde4444d5e23 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..6a5ec54e7bd6 100644
--- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
+++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp
@@ -1169,6 +1169,8 @@ REDHAWK_PALEXPORT bool PalGetMaximumStackBounds(_Out_ void** ppStackLowOut, _Out
status = pthread_attr_get_np(thread, &attr);
#elif HAVE_PTHREAD_GETATTR_NP
status = pthread_getattr_np(thread, &attr);
+#elif defined(HOST_WASM) && defined(FEATURE_WASM_MANAGED_THREADS)
+ // We dont have a pthread_getattr_np, but so far we don't need it.
#else
#error Dont know how to get thread attributes on this platform!
#endif
diff --git a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
index a210ddaa4245..c13efd9b3e21 100644
--- a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
+++ b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.cpp
@@ -191,6 +191,186 @@ 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;
+
+#ifdef TARGET_WASI
+// TODO-LLVM: No-op stubs, maybe when threads are implemented in WASI, we wont have to provide all of these.
+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;
+}
+
+int pthread_attr_init(pthread_attr_t *)
+{
+ // 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();
+ }
+
+ return 0;
+}
+
+int pthread_attr_getstack(pthread_attr_t *, void **stackaddr, size_t *stacksize)
+{
+ unsigned char* pStackLow = &__stack_low;
+ unsigned char* pStackHigh = &__stack_high;
+
+ *stackaddr = pStackLow;
+ *stacksize = pStackHigh - pStackLow;
+ 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*);
+
+// TODO=LLVM: This is a copy of the single threaded implementation.
+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..acbe691e955f 100644
--- a/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h
+++ b/src/coreclr/nativeaot/Runtime/wasm/PalRedhawkWasm.h
@@ -1,6 +1,8 @@
// 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);
+#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..36ba9d7bac4a 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,11 @@
true
true
+
+ true
+ true
+ $(DefineConstants);FEATURE_WASM_MANAGED_THREADS
+ $(DefineConstants);FEATURE_WASM_PERFTRACING
true
@@ -347,6 +352,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/coreclr/runtime.proj b/src/coreclr/runtime.proj
index 0e0bff962b16..332787073e58 100644
--- a/src/coreclr/runtime.proj
+++ b/src/coreclr/runtime.proj
@@ -94,6 +94,10 @@
<_CoreClrBuildArg Include="-cmakeargs "-DCLR_CMAKE_ESRP_CLIENT=$(DotNetEsrpToolPath)"" />
+
+ <_CoreClrBuildArg Include="-cmakeargs "-DCLR_CMAKE_TARGET_OS_SUBGROUP=multithread"" />
+
+
<_CoreClrBuildScript Condition="$([MSBuild]::IsOsPlatform(Windows))">build-runtime.cmd
<_CoreClrBuildScript Condition="!$([MSBuild]::IsOsPlatform(Windows))">build-runtime.sh
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..7f33ff8ae73c 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,30 @@ 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
+ // Required by JavaScript/JSFunctionBinding.cs
+ [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_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_install_js_worker_interop_wrapper")]
+ public static unsafe partial void InstallWebWorkerInterop(nint proxyContextGCHandle, void* beforeSyncJSImport, void* afterSyncJSImport, void* pumpHandler);
+ // Required by JavaScript/JSProxyContext.cs
+ [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();
+ // Required by JavaScript/CancelablePromise.cs
+ [LibraryImport(JSLibrary, EntryPoint = "mono_wasm_cancel_promise_post")]
+ public static unsafe partial void CancelPromisePost(nint targetNativeTID, nint taskHolderGCHandle);
+#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/mono/browser/runtime/loader/polyfills.ts b/src/mono/browser/runtime/loader/polyfills.ts
index 181323b0fe0a..4b230101a57f 100644
--- a/src/mono/browser/runtime/loader/polyfills.ts
+++ b/src/mono/browser/runtime/loader/polyfills.ts
@@ -22,7 +22,8 @@ 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");
+ // TODO-LLVM: Comment this check out for now so we can run at least some tests.
+ //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..1da5b149685d 100644
--- a/src/native/external/zlib-ng.cmake
+++ b/src/native/external/zlib-ng.cmake
@@ -26,8 +26,11 @@ 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)
+ # TODO-LLVM: We can't build zlib-ng with threads enabled for browser/wasi yet, because it requires a libc with pthreads, which we don't have as we use the wasip2 libc.
+ if (CLR_CMAKE_TARGET_BROWSER)
add_compile_options(-pthread)
add_linker_flag(-pthread)
+ endif()
endif()
endif()
diff --git a/src/tests/Common/dirs.proj b/src/tests/Common/dirs.proj
index 7931d4bd7b7b..ab9ede0674d6 100644
--- a/src/tests/Common/dirs.proj
+++ b/src/tests/Common/dirs.proj
@@ -31,6 +31,13 @@
+
+
+
+
+
+
+
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 66652353a200..baff7199bda3 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -490,6 +490,7 @@
$(GroupBuildCmd) "/p:MonoForceInterpreter=true"
$(GroupBuildCmd) "/p:RunAOTCompilation=true"
$(GroupBuildCmd) "/p:DevTeamProvisioning=$(DevTeamProvisioning)"
+ $(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..d6ddbd02905a 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