From 8dc48013ec416e58f4ab77f5b4e91eef8435b9ec Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Wed, 6 Nov 2024 15:25:27 +0100 Subject: [PATCH] Reorganize WASI 0.2.0 bindings to facilitate reuse (#175) This patch doesn't introduce any functional changes: it's purely a change to how the WASI 0.2.0 host API implementation is organized. This will be used in further patches introducing more refactorings in service of more easily building WASI 0.2.x bindings making use of new WASI features. Note that there's one part here that breaks embeddings providing their own host API: the `host_api.cmake` file has moved into the API implementation directory to enable more flexible organization of the implementation. Signed-off-by: Till Schneidereit --- CMakeLists.txt | 2 +- host-apis/wasi-0.2.0/handles.h | 254 ++++++++++++++++++ .../wasi-0.2.0}/host_api.cmake | 1 + host-apis/wasi-0.2.0/host_api.cpp | 241 +---------------- 4 files changed, 257 insertions(+), 241 deletions(-) create mode 100644 host-apis/wasi-0.2.0/handles.h rename {cmake => host-apis/wasi-0.2.0}/host_api.cmake (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93ee5251..ceb49fcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ include("wasmtime") include("fmt") include("spidermonkey") include("openssl") -include("host_api") +include("${HOST_API}/host_api.cmake") include("build-crates") add_library(extension_api INTERFACE include/extension-api.h runtime/encode.h runtime/decode.h) diff --git a/host-apis/wasi-0.2.0/handles.h b/host-apis/wasi-0.2.0/handles.h new file mode 100644 index 00000000..74fbf1a7 --- /dev/null +++ b/host-apis/wasi-0.2.0/handles.h @@ -0,0 +1,254 @@ +/** + * NOT PART OF THE PUBLIC INTERFACE! + * + * Types for dealing with WASI handles in the wit-bindgen generated C bindings. + */ + +#ifndef HANDLES_H +#define HANDLES_H + +#include "host_api.h" +#include "bindings/bindings.h" + +#include +#include +#ifdef DEBUG +#include +#endif + +using host_api::HostString; +using std::optional; +using std::string_view; +using std::tuple; +using std::unique_ptr; +using std::vector; + +// The host interface makes the assumption regularly that uint32_t is sufficient space to store a +// pointer. +static_assert(sizeof(uint32_t) == sizeof(void *)); + +typedef wasi_http_types_own_future_incoming_response_t future_incoming_response_t; +typedef wasi_http_types_borrow_future_incoming_response_t borrow_future_incoming_response_t; + +typedef wasi_http_types_own_incoming_body_t incoming_body_t; +typedef wasi_http_types_own_outgoing_body_t outgoing_body_t; + +using field_key = wasi_http_types_field_key_t; +using field_value = wasi_http_types_field_value_t; + +typedef wasi_io_poll_own_pollable_t own_pollable_t; +typedef wasi_io_poll_borrow_pollable_t borrow_pollable_t; +typedef wasi_io_poll_list_borrow_pollable_t list_borrow_pollable_t; + +#ifdef LOG_HANDLE_OPS +#define LOG_HANDLE_OP(...) \ + fprintf(stderr, "%s", __PRETTY_FUNCTION__); \ + fprintf(stderr, __VA_ARGS__) +#else +#define LOG_HANDLE_OP(...) +#endif + +/// The type of handles used by the host interface. +typedef int32_t Handle; +constexpr Handle POISONED_HANDLE = -1; + +class host_api::HandleState { +protected: + HandleState() = default; + +public: + virtual ~HandleState() = default; + virtual bool valid() const = 0; +}; + +template struct HandleOps {}; + +template class WASIHandle : public host_api::HandleState { +#ifdef DEBUG + static inline auto used_handles = std::set(); +#endif + +protected: + Handle handle_; +#ifdef DEBUG + bool owned_; +#endif + +public: + using Borrowed = typename HandleOps::borrowed; + + explicit WASIHandle(typename HandleOps::owned handle) : handle_{handle.__handle} { + LOG_HANDLE_OP("Creating owned handle %d\n", handle.__handle); +#ifdef DEBUG + owned_ = true; + MOZ_ASSERT(!used_handles.contains(handle.__handle)); + used_handles.insert(handle.__handle); +#endif + } + + explicit WASIHandle(typename HandleOps::borrowed handle) : handle_{handle.__handle} { + LOG_HANDLE_OP("Creating borrowed handle %d\n", handle.__handle); +#ifdef DEBUG + owned_ = false; + MOZ_ASSERT(!used_handles.contains(handle.__handle)); + used_handles.insert(handle.__handle); +#endif + } + + ~WASIHandle() override { +#ifdef DEBUG + if (handle_ != POISONED_HANDLE) { + LOG_HANDLE_OP("Deleting (owned? %d) handle %d\n", owned_, handle_); + MOZ_ASSERT(used_handles.contains(handle_)); + used_handles.erase(handle_); + } +#endif + } + + static WASIHandle *cast(HandleState *handle) { + return reinterpret_cast *>(handle); + } + + typename HandleOps::borrowed borrow(HandleState *handle) { return cast(handle)->borrow(); } + + bool valid() const override { + bool valid = handle_ != POISONED_HANDLE; + MOZ_ASSERT_IF(valid, used_handles.contains(handle_)); + return valid; + } + + typename HandleOps::borrowed borrow() const { + MOZ_ASSERT(valid()); + LOG_HANDLE_OP("borrowing handle %d\n", handle_); + return {handle_}; + } + + typename HandleOps::owned take() { + MOZ_ASSERT(valid()); + MOZ_ASSERT(owned_); + LOG_HANDLE_OP("taking handle %d\n", handle_); + typename HandleOps::owned handle = {handle_}; +#ifdef DEBUG + used_handles.erase(handle_); +#endif + handle_ = POISONED_HANDLE; + return handle; + } +}; + +template struct Borrow { + static constexpr typename HandleOps::borrowed invalid{std::numeric_limits::max()}; + typename HandleOps::borrowed handle_{invalid}; + + explicit Borrow(host_api::HandleState *handle) { + handle_ = WASIHandle::cast(handle)->borrow(); + } + + explicit Borrow(typename HandleOps::borrowed handle) { handle_ = handle; } + + explicit Borrow(typename HandleOps::owned handle) { handle_ = {handle.__handle}; } + + operator typename HandleOps::borrowed() const { return handle_; } +}; + +template <> struct HandleOps { + using owned = wasi_io_poll_own_pollable_t; + using borrowed = wasi_io_poll_borrow_pollable_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_headers_t; + using borrowed = wasi_http_types_borrow_fields_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_incoming_request_t; + using borrowed = wasi_http_types_borrow_incoming_request_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_outgoing_request_t; + using borrowed = wasi_http_types_borrow_outgoing_request_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_future_incoming_response_t; + using borrowed = wasi_http_types_borrow_future_incoming_response_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_incoming_response_t; + using borrowed = wasi_http_types_borrow_incoming_response_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_outgoing_response_t; + using borrowed = wasi_http_types_borrow_outgoing_response_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_incoming_body_t; + using borrowed = wasi_http_types_borrow_incoming_body_t; +}; + +template <> struct HandleOps { + using owned = wasi_http_types_own_outgoing_body_t; + using borrowed = wasi_http_types_borrow_outgoing_body_t; +}; + +struct OutputStream {}; +template <> struct HandleOps { + using owned = wasi_io_streams_own_output_stream_t; + using borrowed = wasi_io_streams_borrow_output_stream_t; +}; + +struct InputStream {}; +template <> struct HandleOps { + using owned = wasi_io_streams_own_input_stream_t; + using borrowed = wasi_io_streams_borrow_input_stream_t; +}; + +class IncomingBodyHandle final : public WASIHandle { + HandleOps::owned stream_handle_; + PollableHandle pollable_handle_; + + friend host_api::HttpIncomingBody; + friend host_api::HttpOutgoingBody; + +public: + explicit IncomingBodyHandle(HandleOps::owned handle) + : WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) { + HandleOps::owned stream{}; + if (!wasi_http_types_method_incoming_body_stream(borrow(), &stream)) { + MOZ_ASSERT_UNREACHABLE("Getting a body's stream should never fail"); + } + stream_handle_ = stream; + } + + static IncomingBodyHandle *cast(HandleState *handle) { + return reinterpret_cast(handle); + } +}; + +class OutgoingBodyHandle final : public WASIHandle { + HandleOps::owned stream_handle_; + PollableHandle pollable_handle_; + + friend host_api::HttpOutgoingBody; + +public: + explicit OutgoingBodyHandle(HandleOps::owned handle) + : WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) { + HandleOps::owned stream{}; + if (!wasi_http_types_method_outgoing_body_write(borrow(), &stream)) { + MOZ_ASSERT_UNREACHABLE("Getting a body's stream should never fail"); + } + stream_handle_ = stream; + } + + static OutgoingBodyHandle *cast(HandleState *handle) { + return reinterpret_cast(handle); + } +}; + +#endif diff --git a/cmake/host_api.cmake b/host-apis/wasi-0.2.0/host_api.cmake similarity index 91% rename from cmake/host_api.cmake rename to host-apis/wasi-0.2.0/host_api.cmake index f18193f0..82b0e323 100644 --- a/cmake/host_api.cmake +++ b/host-apis/wasi-0.2.0/host_api.cmake @@ -8,6 +8,7 @@ add_library(host_api STATIC target_link_libraries(host_api PRIVATE spidermonkey) target_include_directories(host_api PRIVATE include) +target_include_directories(host_api PRIVATE ${HOST_API}) target_include_directories(host_api PUBLIC ${HOST_API}/include) if (CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/host-apis/wasi-0.2.0/host_api.cpp b/host-apis/wasi-0.2.0/host_api.cpp index 6a7d2c56..bd2772e9 100644 --- a/host-apis/wasi-0.2.0/host_api.cpp +++ b/host-apis/wasi-0.2.0/host_api.cpp @@ -1,245 +1,6 @@ #include "host_api.h" #include "bindings/bindings.h" - -#include -#include -#ifdef DEBUG -#include -#endif - -using host_api::HostString; -using std::optional; -using std::string_view; -using std::tuple; -using std::unique_ptr; -using std::vector; - -// The host interface makes the assumption regularly that uint32_t is sufficient space to store a -// pointer. -static_assert(sizeof(uint32_t) == sizeof(void *)); - -typedef wasi_http_types_own_future_incoming_response_t future_incoming_response_t; -typedef wasi_http_types_borrow_future_incoming_response_t borrow_future_incoming_response_t; - -typedef wasi_http_types_own_incoming_body_t incoming_body_t; -typedef wasi_http_types_own_outgoing_body_t outgoing_body_t; - -using field_key = wasi_http_types_field_key_t; -using field_value = wasi_http_types_field_value_t; - -typedef wasi_io_poll_own_pollable_t own_pollable_t; -typedef wasi_io_poll_borrow_pollable_t borrow_pollable_t; -typedef wasi_io_poll_list_borrow_pollable_t list_borrow_pollable_t; - -#ifdef LOG_HANDLE_OPS -#define LOG_HANDLE_OP(...) \ - fprintf(stderr, "%s", __PRETTY_FUNCTION__); \ - fprintf(stderr, __VA_ARGS__) -#else -#define LOG_HANDLE_OP(...) -#endif - -/// The type of handles used by the host interface. -typedef int32_t Handle; -constexpr Handle POISONED_HANDLE = -1; - -class host_api::HandleState { -protected: - HandleState() = default; - -public: - virtual ~HandleState() = default; - virtual bool valid() const = 0; -}; - -template struct HandleOps {}; - -template class WASIHandle : public host_api::HandleState { -#ifdef DEBUG - static inline auto used_handles = std::set(); -#endif - -protected: - Handle handle_; -#ifdef DEBUG - bool owned_; -#endif - -public: - using Borrowed = typename HandleOps::borrowed; - - explicit WASIHandle(typename HandleOps::owned handle) : handle_{handle.__handle} { - LOG_HANDLE_OP("Creating owned handle %d\n", handle.__handle); -#ifdef DEBUG - owned_ = true; - MOZ_ASSERT(!used_handles.contains(handle.__handle)); - used_handles.insert(handle.__handle); -#endif - } - - explicit WASIHandle(typename HandleOps::borrowed handle) : handle_{handle.__handle} { - LOG_HANDLE_OP("Creating borrowed handle %d\n", handle.__handle); -#ifdef DEBUG - owned_ = false; - MOZ_ASSERT(!used_handles.contains(handle.__handle)); - used_handles.insert(handle.__handle); -#endif - } - - ~WASIHandle() override { -#ifdef DEBUG - if (handle_ != POISONED_HANDLE) { - LOG_HANDLE_OP("Deleting (owned? %d) handle %d\n", owned_, handle_); - MOZ_ASSERT(used_handles.contains(handle_)); - used_handles.erase(handle_); - } -#endif - } - - static WASIHandle *cast(HandleState *handle) { - return reinterpret_cast *>(handle); - } - - typename HandleOps::borrowed borrow(HandleState *handle) { return cast(handle)->borrow(); } - - bool valid() const override { - bool valid = handle_ != POISONED_HANDLE; - MOZ_ASSERT_IF(valid, used_handles.contains(handle_)); - return valid; - } - - typename HandleOps::borrowed borrow() const { - MOZ_ASSERT(valid()); - LOG_HANDLE_OP("borrowing handle %d\n", handle_); - return {handle_}; - } - - typename HandleOps::owned take() { - MOZ_ASSERT(valid()); - MOZ_ASSERT(owned_); - LOG_HANDLE_OP("taking handle %d\n", handle_); - typename HandleOps::owned handle = {handle_}; -#ifdef DEBUG - used_handles.erase(handle_); -#endif - handle_ = POISONED_HANDLE; - return handle; - } -}; - -template struct Borrow { - static constexpr typename HandleOps::borrowed invalid{std::numeric_limits::max()}; - typename HandleOps::borrowed handle_{invalid}; - - explicit Borrow(host_api::HandleState *handle) { - handle_ = WASIHandle::cast(handle)->borrow(); - } - - explicit Borrow(typename HandleOps::borrowed handle) { handle_ = handle; } - - explicit Borrow(typename HandleOps::owned handle) { handle_ = {handle.__handle}; } - - operator typename HandleOps::borrowed() const { return handle_; } -}; - -template <> struct HandleOps { - using owned = wasi_io_poll_own_pollable_t; - using borrowed = wasi_io_poll_borrow_pollable_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_headers_t; - using borrowed = wasi_http_types_borrow_fields_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_incoming_request_t; - using borrowed = wasi_http_types_borrow_incoming_request_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_outgoing_request_t; - using borrowed = wasi_http_types_borrow_outgoing_request_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_future_incoming_response_t; - using borrowed = wasi_http_types_borrow_future_incoming_response_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_incoming_response_t; - using borrowed = wasi_http_types_borrow_incoming_response_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_outgoing_response_t; - using borrowed = wasi_http_types_borrow_outgoing_response_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_incoming_body_t; - using borrowed = wasi_http_types_borrow_incoming_body_t; -}; - -template <> struct HandleOps { - using owned = wasi_http_types_own_outgoing_body_t; - using borrowed = wasi_http_types_borrow_outgoing_body_t; -}; - -struct OutputStream {}; -template <> struct HandleOps { - using owned = wasi_io_streams_own_output_stream_t; - using borrowed = wasi_io_streams_borrow_output_stream_t; -}; - -struct InputStream {}; -template <> struct HandleOps { - using owned = wasi_io_streams_own_input_stream_t; - using borrowed = wasi_io_streams_borrow_input_stream_t; -}; - -class IncomingBodyHandle final : public WASIHandle { - HandleOps::owned stream_handle_; - PollableHandle pollable_handle_; - - friend host_api::HttpIncomingBody; - -public: - explicit IncomingBodyHandle(HandleOps::owned handle) - : WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) { - HandleOps::owned stream{}; - if (!wasi_http_types_method_incoming_body_stream(borrow(), &stream)) { - MOZ_ASSERT_UNREACHABLE("Getting a body's stream should never fail"); - } - stream_handle_ = stream; - } - - static IncomingBodyHandle *cast(HandleState *handle) { - return reinterpret_cast(handle); - } -}; - -class OutgoingBodyHandle final : public WASIHandle { - HandleOps::owned stream_handle_; - PollableHandle pollable_handle_; - - friend host_api::HttpOutgoingBody; - -public: - explicit OutgoingBodyHandle(HandleOps::owned handle) - : WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) { - HandleOps::owned stream{}; - if (!wasi_http_types_method_outgoing_body_write(borrow(), &stream)) { - MOZ_ASSERT_UNREACHABLE("Getting a body's stream should never fail"); - } - stream_handle_ = stream; - } - - static OutgoingBodyHandle *cast(HandleState *handle) { - return reinterpret_cast(handle); - } -}; +#include "handles.h" size_t api::AsyncTask::select(std::vector &tasks) { auto count = tasks.size();