Skip to content

[NativeAOT-LLVM] Wasm managed threads - build new nupkg #3060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: feature/NativeAOT-LLVM
Choose a base branch
from

Conversation

yowl
Copy link
Contributor

@yowl yowl commented Mar 30, 2025

This PR provides a base for working on multithread support, as and when we have Wasm runtimes that support threads. With the browser this could be done now using Web Workers, the same as mono, for WASI with wasmtime we will need to wait for at least preview3 v2, possibly later.

No actual work for the dotnet runtime functionality that we will need, e.g. GC polling is included, this PR just creates CI jobs, runs the tests that passed, and creates 2 new packages for testing.

@jkotas jkotas added the area-NativeAOT-LLVM LLVM generation for Native AOT compilation (including Web Assembly) label Apr 27, 2025
@@ -76,6 +76,11 @@
<Compile Include="System\Runtime\InteropServices\JavaScript\JSWebWorker.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\JSSynchronizationContext.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\JSAsyncTaskScheduler.cs" />
<!-- Can't build System.Threading.Channels.csproj without these -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that? This workaround does not look right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking, it was as though even though these transitive dependencies were built, they weren't found for this project. However it doesn't seem consistent and I removed this change now.

@yowl yowl closed this Jul 3, 2025
@yowl yowl reopened this Jul 3, 2025
@yowl yowl force-pushed the FEATURE_WASM_MANAGED_THREADS branch from cba3e94 to 494d4c5 Compare July 5, 2025 12:42
@yowl yowl closed this Jul 5, 2025
@yowl yowl reopened this Jul 5, 2025
@yowl yowl closed this Jul 5, 2025
@yowl yowl reopened this Jul 5, 2025
@yowl yowl marked this pull request as ready for review July 5, 2025 18:39
@yowl
Copy link
Contributor Author

yowl commented Jul 5, 2025

Cc @dotnet/nativeaot-llvm

Copy link

@SingleAccretion SingleAccretion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a bit of a skim so far, will continue tomorrow.

@@ -500,6 +500,44 @@ jobs:
buildConfig: ${{ parameters.buildConfig }}
${{ insert }}: ${{ parameters.jobParameters }}

- ${{ if containsValue(parameters.platforms, 'wasi_multithread_wasm_win') }}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need these new platforms; we should be able to use the existing ones like here: https://github.com/dotnet/runtime/blob/9e5e6aa7bc36aeb2a154709a9d1192030c30a2ef/eng/pipelines/common/templates/wasm-build-only.yml#L17-L36, utilizing the nameSuffix parameter to global-build-job.yml.


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 -cmakeargs -DCLR_CMAKE_TARGET_OS_SUBGROUP=multithread '/p:WasmEnableThreads=true'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should funnel /p:WasmEnableThreads down to cmake/clang automatically. E. g. the libs native build sets CMAKE_USE_PTHREADS, we can extended that pattern to the CoreCLR's runtime.proj.

@@ -31,8 +31,17 @@ steps:
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 eq(parameters.platform, 'browser_multithread_wasm_win') }}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to avoid this duplication. We can add a parameter to these post-build steps like wasmEnableThreadsArg: /p:WasmEnableThreads=true and use it here (and below to excluded the libs tests).

@@ -438,6 +438,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<CompileWasmArgs Condition="'$(NativeDebugSymbols)' == 'true'">$(CompileWasmArgs) -g3</CompileWasmArgs>
<CompileWasmArgs Condition="'$(WasmEnableNonTrappingFloatToIntConversions)' == 'true'">$(CompileWasmArgs) -mnontrapping-fptoint</CompileWasmArgs>
<CompileWasmArgs Condition="'$(IlcLlvmExceptionHandlingModel)' == 'wasm'">$(CompileWasmArgs) -fwasm-exceptions</CompileWasmArgs>
<CompileWasmArgs Condition="'$(WasmEnableThreads)' == 'true'">$(CompileWasmArgs) -pthread -matomics -mbulk-memory</CompileWasmArgs>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to modify the IlcLlvmTarget to include -threads I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think there were any triples for threads, at least not yet.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is wasm32-wasi-threads, but looks like we don't need anything for Emscripten indeed and -pthread is enough.

Comment on lines 1154 to 1155
#elif defined(HOST_WASM) && defined(FEATURE_WASM_MANAGED_THREADS)
PalGetMaximumStackBounds_MultiThreadedWasm(&pStackLowOut, &pStackHighOut);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the pthread_attr_init code from below instead of a bespoke version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, have updated this and removed PalGetMaximumStackBounds_MultiThreadedWasm with the logic move to pthread_attr_init and pthread_attr_getstack

Comment on lines 33 to 34
add_compile_options(-D_WASI_EMULATED_PTHREAD)
add_linker_flag(-Wl,-lwasi-emulated-pthread)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why didn't we need this before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we don't need it after all. Removed.

@yowl
Copy link
Contributor Author

yowl commented Jul 11, 2025

What is it about PackagingTests.csproj that changes the way the target runtime package name is generated?

@SingleAccretion
Copy link

SingleAccretion commented Jul 11, 2025

What is it about PackagingTests.csproj that changes the way the target runtime package name is generated?

The packaging test implementation project (HelloWasm\PackagingTests\PackagingTestProject.csproj) receives the things it needs from the parent project (HelloWasm\PackagingTests.csproj) via the Test_ properties. The target package name, however, is actually not decided by these properties but by the target RID via nuget runtime.json magic. Since we don't have a target RID for multi-threaded WASM (a bit of a design flaw in the current scheme, IMO, but that ship has sailed long ago), we're in a bit of a pickle. Maybe disable the packaging test for MT and leave solving this problem for later?

Copy link

@SingleAccretion SingleAccretion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Larger overall point on testing.

We currently don't have even the basic support for e. g. correct memory orderings, so stuff will fail due to race conditions randomly in CI. Should we disable all tests (and only do build) for now, before we know the configuration is at least not 100% known to be flaky? Alternatively, only enable like one test (e. g. HelloWasm)?

Comment on lines 68 to 72
if defined __CMakeArgs (
if not "!__CMakeArgs:multithread=!" == "!__CMakeArgs!" (
set __ExtraCmakeParams=%__ExtraCmakeParams% -D_WASI_EMULATED_PTHREAD
)
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed?

@@ -17,21 +18,25 @@ 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
displayName: Build target packages
- ${{ if ne(parameters.nameSuffix, 'Multithreaded') }}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use wasmEnableThreadsArg here.


# Build host packages.
- script: $(Build.SourcesDirectory)/build$(scriptExt) libs+nativeaot.packages -a $(hostedTargetArch) -c $(buildConfigUpper) -cross $(_officialBuildParameter) -ci
displayName: Build host packages

# 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, ''), ne(parameters.nameSuffix, 'Multithreaded'))) }}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use wasmEnableThreadsArg != '' here, the existing nameSuffix check is already a bit of a hack.

@@ -49,7 +54,7 @@ steps:
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(and(eq(parameters.archType, 'wasm'), eq(parameters.nameSuffix, '')), not(eq(parameters.nameSuffix, 'Multithreaded'))) }}:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto with wasmEnableThreadsArg.

osSubgroup: multithread
platforms:
- browser_wasm_win
- wasi_wasm_win

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this going to work with WASI, considering we don't have a -threads triple for WASIp2 yet? I would focus on making the fundamentals work on browser for now, we can enable WASI later when the libc support comes along.

Comment on lines +195 to +208
#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;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of code copied here and it's not clear what part of it is needed and what part is there because WASI doesn't actually -pthread support yet.

@@ -35,6 +35,11 @@
<PropertyGroup Condition="'$(TargetsSingleThreadedWasm)' != 'true'">
<FeaturePortableThreadPool>true</FeaturePortableThreadPool>
<FeaturePortableTimer>true</FeaturePortableTimer>
<!-- LLVM-TODO: Remove FeatureWasmManagedThreads in favor of WasmEnableThreads. -->
<FeatureWasmManagedThreads Condition="'$(WasmEnableThreads)' == 'true'">true</FeatureWasmManagedThreads>
<FeatureWasmPerfTracing Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and ('$(WasmEnableThreads)' == 'true')">true</FeatureWasmPerfTracing>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is FeatureWasmPerfTracing actually used for anything? We don't really have any tracing support...

Comment on lines +355 to +356
<Compile Include="System\Threading\PortableThreadPool.NativeAot.Browser.cs" Condition="'$(TargetsBrowser)' == 'true' and '$(WasmEnableThreads)' == 'true'" />
<Compile Include="System\Threading\ThreadPool.NativeAot.Browser.cs" Condition="'$(TargetsBrowser)' == 'true' and '$(WasmEnableThreads)' == 'true'" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the portable thread pool files insufficient and we need these as well?

Comment on lines +29 to +32
if (CLR_CMAKE_TARGET_BROWSER)
add_compile_options(-pthread)
add_linker_flag(-pthread)
endif()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the above comment of not fixing WASI before it can be fixed properly, I think we can remove this condition as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmake seems broken with this for wasi

  [vcvarsall.bat] Environment initialized for: 'arm64'
  Commencing build of native components

  Not searching for unused variables given on the command line.
  -- The C compiler identification is Clang 19.1.5
  -- Detecting C compiler ABI info
  -- Detecting C compiler ABI info - done
  -- Check for working C compiler: c:/github/wasi-sdk25/bin/clang.exe - skipped
  -- Detecting C compile features
  -- Detecting C compile features - done
  -- Performing Test COMPILER_SUPPORTS_W_RESERVED_IDENTIFIER
  -- Performing Test COMPILER_SUPPORTS_W_RESERVED_IDENTIFIER - Success
  -- Looking for include files sys/auxv.h, asm/hwcap.h
  -- Looking for include files sys/auxv.h, asm/hwcap.h - not found
  -- Looking for sysctlbyname
  -- Looking for sysctlbyname - not found
  -- Looking for arc4random_buf
  -- Looking for arc4random_buf - found
  -- Looking for O_CLOEXEC
  -- Looking for O_CLOEXEC - found
  -- Looking for include file windows.h
  -- Looking for include file windows.h - not found
  -- Looking for include files windows.h, bcrypt.h
  -- Looking for include files windows.h, bcrypt.h - not found
  -- Looking for clock_gettime_nsec_np
  -- Looking for clock_gettime_nsec_np - not found
  -- Using CMake version 4.0.1
  -- ZLIB_HEADER_VERSION: 1.3.1
  -- ZLIBNG_HEADER_VERSION: 2.2.1
  -- Arch detected: 'wasm32-wasip2'
  -- Basearch of 'wasm32-wasip2' has been detected as: 'wasm32'
  -- Using CMake toolchain: C:/github/wasi-sdk25/share/cmake/wasi-sdk-p2.cmake
  -- Performing Test FNO_LTO_AVAILABLE
  -- Performing Test FNO_LTO_AVAILABLE - Failed
  -- Looking for arm_acle.h
  -- Looking for arm_acle.h - not found
  -- Looking for sys/auxv.h
  -- Looking for sys/auxv.h - not found
  -- Looking for sys/sdt.h
  -- Looking for sys/sdt.h - not found
  -- Looking for unistd.h
  -- Looking for unistd.h - not found
  -- Looking for linux/auxvec.h
  -- Looking for linux/auxvec.h - not found
  -- Looking for sys/types.h
  -- Looking for sys/types.h - not found
  -- Looking for stdint.h
  -- Looking for stdint.h - not found
  -- Looking for stddef.h
  -- Looking for stddef.h - not found
  -- Check size of off64_t
  -- Check size of off64_t - failed
  -- Check size of _off64_t
  -- Check size of _off64_t - failed
  -- Check size of __off64_t
  -- Check size of __off64_t - failed
  -- Looking for fseeko
  -- Looking for fseeko - not found
  -- Looking for strerror
  -- Looking for strerror - not found
  -- Looking for posix_memalign
  -- Looking for posix_memalign - not found
  -- Performing Test HAVE_NO_INTERPOSITION
  -- Performing Test HAVE_NO_INTERPOSITION - Failed
  -- Performing Test HAVE_ATTRIBUTE_VISIBILITY_HIDDEN
  -- Performing Test HAVE_ATTRIBUTE_VISIBILITY_HIDDEN - Failed
  -- Performing Test HAVE_ATTRIBUTE_VISIBILITY_INTERNAL
  -- Performing Test HAVE_ATTRIBUTE_VISIBILITY_INTERNAL - Failed
  -- Performing Test HAVE_ATTRIBUTE_ALIGNED
  -- Performing Test HAVE_ATTRIBUTE_ALIGNED - Failed
  -- Performing Test HAVE_BUILTIN_ASSUME_ALIGNED
  -- Performing Test HAVE_BUILTIN_ASSUME_ALIGNED - Failed
  -- Performing Test HAVE_BUILTIN_CTZ
  -- Performing Test HAVE_BUILTIN_CTZ - Failed
  -- Performing Test HAVE_BUILTIN_CTZLL
  -- Performing Test HAVE_BUILTIN_CTZLL - Failed
  -- Performing Test HAVE_PTRDIFF_T
  -- Performing Test HAVE_PTRDIFF_T - Failed
  -- Check size of void *
  -- Check size of void * - failed
  -- sizeof(void *) is  bytes
  CMake Error at C:/github/runtimelab/src/native/external/zlib-ng/CMakeLists.txt:556 (message):
    sizeof(void *) is neither 32 nor 64 bit

@@ -489,6 +489,7 @@ jobs:
helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }}
variables: ${{ parameters.variables }}
osGroup: wasi
osSubGroup: ${{ parameters.osSubGroup }}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed (also below)?

@yowl
Copy link
Contributor Author

yowl commented Jul 15, 2025

Larger overall point on testing.

We currently don't have even the basic support for e. g. correct memory orderings, so stuff will fail due to race conditions randomly in CI. Should we disable all tests (and only do build) for now, before we know the configuration is at least not 100% known to be flaky? Alternatively, only enable like one test (e. g. HelloWasm)?

Ok, that's fine with me. Will remove in next commit.

Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-NativeAOT-LLVM LLVM generation for Native AOT compilation (including Web Assembly)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants