Skip to content
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

[NativeAOT-LLVM] support System.Net.Http.HttpClient on WASIp2 #2614

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

Commits on Aug 26, 2024

  1. support System.Net.Http.HttpClient on WASIp2

    This adds `WasiHttpHandler`, a new implementation of `HttpMessageHandler` based
    on
    [wasi:http/outgoing-handler](https://github.com/WebAssembly/wasi-http/blob/v0.2.0/wit/handler.wit),
    plus tweaks to `System.Threading` to allow async `Task`s to work in a
    single-threaded context, with `ThreadPool` work items dispatched from an
    application-provided event loop.
    
    WASIp2 supports asynchronous I/O and timers via `wasi:io/poll/pollable` resource
    handles.  One or more of those handles may be passed to `wasi:io/poll/poll`,
    which will block until at least one of them is ready.  In order to make this
    model play nice with C#'s `async`/`await` and `Task` features, we need to
    reconcile several constraints:
    
    - WASI is currently single-threaded, and will continue to be that way for a while.
    - C#'s `async`/`await` and `Task` features require a working `ThreadPool` implementation capable of deferring work.
    - A WASI component can export an arbitrary number of functions to the host, and though they will always be called synchronously from the host, they need to be able to perform asynchronous operations before returning.
    - WASIp3 (currently in the design and prototype phase) will support asynchronous exports, with the top level event loop running in the host instead of the guest, and `wasi:io/poll/pollable` will no longer exist.  Therefore, we don't want to add any temporary public APIs to the .NET runtime which will become obsolete when WASIp3 arrives.
    
    The solution we arrived at looks something like this:
    
    - Tweak the existing `ThreadPool` implementation for WASI so that methods such as `RequestWorkerThread` don't throw `PlatformNotSupportedException`s (i.e. allow work items to be queued even though the "worker thread" is always the same one that is queuing the work)
    - Add two new methods to `Thread`:
        - `internal static void Dispatch`: Runs an iteration of event loop, draining the `ThreadPool` queue of ready work items and calling `wasi:io/poll/poll` with any accumulated `pollable` handles
        - `internal static Task Register(int pollableHandle)`: Registers the specified `pollable` handle to be `poll`ed during the next call to `Dispatch`
        - Note that these methods are `internal` because they're temporary and should not be part of the public API, but they are intended to be called via `UnsafeAccessor` by application code (or more precisely, code generated by `wit-bindgen` for the application)
    
    The upshot is that application code can use `wit-bindgen` (either directly or
    via the new `componentize-dotnet` package) to generate async export bindings
    which will provide an event loop backed by `Thread.Dispatch`.  Additionally,
    `wit-bindgen` can transparently convert any `pollable` handles returned by WASI
    imports into `Task`s via `Thread.Register`, allowing the component to `await`
    them, pass them to a combinator such as `Task.WhenEach`, etc.
    
    Later, when WASIp3 arrives and we update the .NET runtime to target it, we'll be
    able to remove some of this code (and the corresponding code in `wit-bindgen`)
    without requiring significant changes to the application developer's experience.
    
    This PR contains a few C# source files that were generated by `wit-bindgen` from
    the official WASI WIT files, plus scripts to regenerate them if desired.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    switch to `wasm32-wasip2` and update WASI test infra
    
    Now that we're using WASI-SDK 22, we can target `wasm32-wasip2`, which produces
    components by default and includes full `wasi:sockets` support.  In order to run
    those components, I've updated the test infrastructure to use Wasmtime 21 (the
    latest release as of this writing).
    
    Other changes of note:
    
    - Tweaked src/coreclr/jit/compiler.cpp to make `Debug` builds work on Linux
    
    - Added libWasiHttp.a, which includes the encoded component type (in the form of a pre-generated Wasm object file) and a `cabi_realloc` definition.  Both of these are generated by `wit-bindgen` and required by `wasm-component-ld` to generate a valid component.
    
    - Added a `FindWasmHostExecutableAndRun.sh` script for running the WASI tests on UNIX-style platforms.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    various WASI build tweaks
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    quote libWasiHttp.a path in custom linker arg
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    fix wasm-component-ld download command
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    tweak EmccExtraArgs in CustomMain.csproj so wasm-component-ld understands it
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    update CMake minimum version in wasi-sdk-p2.cmake
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    use `HeaderDescriptor` to sort content and response headers
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    allow building native WASI test code in src/tests/build.sh
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    allow WASI runtime tests to be built and run on non-Windows systems
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    update runtime tests to work with WASIp2
    
    As of this writing, WASIp2 [does not support process exit
    statuses](WebAssembly/wasi-cli#11) beyond 0 (success)
    and 1 (failure).  To work around this, I've modified the relevant tests to
    return 0 on success instead of 100.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    fix CI for Windows builds; remove unused file
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    disable sprintf warning in llvmlssa.cpp on macOS
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    remove LibraryWorld_cabi_realloc.o
    
    I didn't mean to add this to Git.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    rename `generate-bindings.sh` files for clarity
    
    This makes it more obvious that, though they are similar, they each have a
    different job.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    update to `wit-bindgen` 0.27.0 and regenerate bindings
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    reorganize code; add HttpClient smoke test
    
    - move System/WASIp2 to System/Threading/WASIp2
    - remove generated `cabi_realloc` functions since `wasi-libc` will provide one
    - add HttpClient test to SmokeTests/SharedLibrary
    
    Note that I put the HttpClient test in SmokeTests/SharedLibrary since we were
    already using NodeJS for that test, and adding a simple loopback webserver to
    SharedLibraryDriver.mjs was easiest option available to keep the whole test
    self-contained.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    implement SystemNative_SysLog for WASI
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    increase NodeJS stack trace limit to 200
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    give guest no filesystem access in SharedLibraryDriver.mjs
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    switch to Trace.Assert into HttpClient smoke test
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    rename WASIp2 directory to Wasi
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    fix non-GET methods and add HttpClient echo test
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    use azure NPM
    
    rename
    
    - WasiEventLoop.RegisterWasiPollable
    - WasiEventLoop.DispatchWasiEventLoop
    
    to make it less confusing on the Thread class
    
    - unification of gen-buildsys
    
    - cleanup pal_process_wasi.c
    
    fix build?
    
    more
    
    buffer /echo request body in SharedLibraryDriver.mjs
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    fix gen-buildsys.sh regression
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    allow only infinite `HttpClient.Timeout`s on WASI
    
    This temporary code will be reverted once we support `System.Threading.Timer` on
    WASI in a forthcoming PR.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    use `&` operator to simplify install-jco.ps1
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    remove redundant `CheckWasmSdks` target from SharedLibrary.csproj
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    split `FindWasmHostExecutable.sh` out of `FindWasmHostExecutableAndRun.sh`
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    replace component type object files with WIT files
    
    This updates `wit-bindgen` and `wasm-component-ld`, which now support producing
    and consuming component type WIT files as an alternative to binary object files.
    These files are easier to audit from a security perspective.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    preserve slashes in path in SharedLibrary.csproj
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    temporarily disable ThreadPoolWorkQueue.Dispatch assertion
    
    See dotnet/runtime#104803
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    update `wit-bindgen` to version 0.28.0
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    upgrade to wasi-sdk 24 and wit-bindgen 0.29.0
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    check for WASI in `PhysicalFileProvider.CreateFileWatcher`
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    switch back to WASI 0.2.0
    
    0.2.1 is not yet widely supported, and causes
    [trouble](bytecodealliance/jco#486) for Jco, which
    rely on for the `SharedLibrary` test.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    remove use of `WeakReference` from `WasiEventLoop`
    
    This was causing `HttpClient` timeout tests in the `SharedLibrary` smoke test
    suite to fail, apparently due to `TimerQueue.SetNextTimer` calling
    `WasiEventLoop.RegisterWasiPollable`, attaching a continuation to the resulting
    `Task` and then letting go of the reference, allowing it to be GC'd.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    skip unsupported signal handling on WASI
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    throw PlatformNotSupportedException in ManualResetEventSlim.Wait on WASI
    
    Otherwise, we end up in an infinite loop.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    Revert "switch back to WASI 0.2.0"
    
    This reverts commit a8608b4.
    
    enable `NameResolution` and `Sockets` on WASI
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    
    set `SocketsHttpHandler.IsEnabled` to `false` on WASI
    
    ...at least until we get `System.Net.Sockets` working.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 26, 2024
    Configuration menu
    Copy the full SHA
    beabe5a View commit details
    Browse the repository at this point in the history
  2. make HttpClient.Timeout actually work on WASI

    This requires passing the `CancellationToken` to `WasiEventLoop` and checking it
    before polling.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 26, 2024
    Configuration menu
    Copy the full SHA
    28d758f View commit details
    Browse the repository at this point in the history
  3. fix file count confusion in SharedLibraryDriver.mjs

    Sometimes there are two Wasm files in Jco's output; sometimes three.  In any
    case, hard-coding the number won't fly.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 26, 2024
    Configuration menu
    Copy the full SHA
    e32a223 View commit details
    Browse the repository at this point in the history

Commits on Aug 27, 2024

  1. fix Linux tests

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 27, 2024
    Configuration menu
    Copy the full SHA
    e79642e View commit details
    Browse the repository at this point in the history
  2. use TaskCompletionSource.SetCanceled instead of SetException

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 27, 2024
    Configuration menu
    Copy the full SHA
    2f3ce60 View commit details
    Browse the repository at this point in the history

Commits on Aug 28, 2024

  1. use Stopwatch in HttpClient test to verify timeout works as expected

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 28, 2024
    Configuration menu
    Copy the full SHA
    18f9f6c View commit details
    Browse the repository at this point in the history
  2. provide more informative exception messages in WasiHttpHandler

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 28, 2024
    Configuration menu
    Copy the full SHA
    ebdbabf View commit details
    Browse the repository at this point in the history
  3. remove obsolete TODO-LLVM comments

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 28, 2024
    Configuration menu
    Copy the full SHA
    9d9ed59 View commit details
    Browse the repository at this point in the history

Commits on Aug 29, 2024

  1. update wit-bindgen and regenerate bindings

    See bytecodealliance/wit-bindgen#1040
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    4879365 View commit details
    Browse the repository at this point in the history
  2. to match dotnet/runtime#107096

    pavelsavara authored and dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    137614e View commit details
    Browse the repository at this point in the history
  3. fix disposal of pollable

    pavelsavara authored and dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    b25bc9e View commit details
    Browse the repository at this point in the history
  4. update wit-bindgen and regenerate bindings (again)

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    5f35285 View commit details
    Browse the repository at this point in the history
  5. specify zero expected exit code for WASIp2 smoke test

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    ddbd68a View commit details
    Browse the repository at this point in the history
  6. convert response headers after setting content

    Otherwise, we end up overwriting `response.Content.Headers`.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    e989a63 View commit details
    Browse the repository at this point in the history
  7. exit DispatchWasiEventLoop without polling if tasks have been canceled

    If one or more tasks have been canceled during the call to
    `ThreadPoolWorkQueue.Dispatch`, one or more tasks of interest to the application
    may have completed, so we return control immediately without polling, allowing
    the app to exit if it chooses.
    
    A practical example of this is in the SharedLibrary smoke test.  Without this
    patch, that test will take over 100 seconds to complete, whereas with this patch
    it completes in under a second.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    98bf4dc View commit details
    Browse the repository at this point in the history
  8. assert HTTP test in SharedLibrary completes promptly

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Aug 29, 2024
    Configuration menu
    Copy the full SHA
    757eeb9 View commit details
    Browse the repository at this point in the history

Commits on Sep 3, 2024

  1. support reading trailers in WasiInputStream

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Sep 3, 2024
    Configuration menu
    Copy the full SHA
    dfdd8bc View commit details
    Browse the repository at this point in the history
  2. increase max elapsed time in SharedLibrary test

    ...and hopefully make CI happier.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Sep 3, 2024
    Configuration menu
    Copy the full SHA
    1736bdd View commit details
    Browse the repository at this point in the history

Commits on Sep 5, 2024

  1. work around possible wasmtime-wasi-http bug regarding trailing headers

    I'm still investigating whether this actually _is_ a `wasmtime-wasi-http` bug;
    stay tuned.
    
    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Sep 5, 2024
    Configuration menu
    Copy the full SHA
    dd5f8ae View commit details
    Browse the repository at this point in the history

Commits on Sep 13, 2024

  1. remove temporary http-p2 smoke test

    Signed-off-by: Joel Dice <joel.dice@fermyon.com>
    dicej committed Sep 13, 2024
    Configuration menu
    Copy the full SHA
    68ec226 View commit details
    Browse the repository at this point in the history