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