From 2ef881e5e8e10632ec4716186e088644935dbc6d Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 6 Nov 2024 14:07:16 -0800 Subject: [PATCH] merge --- host-apis/wasi-0.2.2/handles.h | 254 +++++++++++++++++++++ host-apis/wasi-0.2.2/host_api.cmake | 19 ++ host-apis/wasi-0.2.2/host_api.cpp | 328 ++++++---------------------- 3 files changed, 336 insertions(+), 265 deletions(-) create mode 100644 host-apis/wasi-0.2.2/handles.h create mode 100644 host-apis/wasi-0.2.2/host_api.cmake diff --git a/host-apis/wasi-0.2.2/handles.h b/host-apis/wasi-0.2.2/handles.h new file mode 100644 index 00000000..74fbf1a7 --- /dev/null +++ b/host-apis/wasi-0.2.2/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/host-apis/wasi-0.2.2/host_api.cmake b/host-apis/wasi-0.2.2/host_api.cmake new file mode 100644 index 00000000..82b0e323 --- /dev/null +++ b/host-apis/wasi-0.2.2/host_api.cmake @@ -0,0 +1,19 @@ +add_library(host_api STATIC + ${HOST_API}/host_api.cpp + ${HOST_API}/host_call.cpp + ${HOST_API}/bindings/bindings.c + ${HOST_API}/bindings/bindings_component_type.o + ${CMAKE_CURRENT_SOURCE_DIR}/include/host_api.h +) + +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") + set(ADAPTER "debug") +else() + set(ADAPTER "release") +endif() +set(ADAPTER "${HOST_API}/preview1-adapter-${ADAPTER}/wasi_snapshot_preview1.wasm") diff --git a/host-apis/wasi-0.2.2/host_api.cpp b/host-apis/wasi-0.2.2/host_api.cpp index cef39f56..d67e322f 100644 --- a/host-apis/wasi-0.2.2/host_api.cpp +++ b/host-apis/wasi-0.2.2/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(); @@ -574,31 +335,73 @@ void HttpOutgoingBody::write(const uint8_t *bytes, size_t len) { MOZ_RELEASE_ASSERT(write_to_outgoing_body(borrow, bytes, len)); } -Result HttpOutgoingBody::write_all(const uint8_t *bytes, size_t len) { - if (!valid()) { - // TODO: proper error handling for all 154 error codes. - return Result::err(154); +class BodyWriteAllTask final : public api::AsyncTask { + HttpOutgoingBody *outgoing_body_; + PollableHandle outgoing_pollable_; + + api::TaskCompletionCallback cb_; + Heap cb_receiver_; + HostBytes bytes_; + size_t offset_ = 0; + +public: + explicit BodyWriteAllTask(HttpOutgoingBody *outgoing_body, HostBytes bytes, + api::TaskCompletionCallback completion_callback, + HandleObject callback_receiver) + : outgoing_body_(outgoing_body), cb_(completion_callback), + cb_receiver_(callback_receiver), bytes_(std::move(bytes)) { + outgoing_pollable_ = outgoing_body_->subscribe().unwrap(); } - auto *state = static_cast(handle_state_.get()); - Borrow borrow(state->stream_handle_); + [[nodiscard]] bool run(api::Engine *engine) override { + MOZ_ASSERT(offset_ < bytes_.len); + while (true) { + auto res = outgoing_body_->capacity(); + if (res.is_err()) { + return false; + } + uint64_t capacity = res.unwrap(); + if (capacity == 0) { + engine->queue_async_task(this); + return true; + } - while (len > 0) { - auto capacity_res = capacity(); - if (capacity_res.is_err()) { - // TODO: proper error handling for all 154 error codes. - return Result::err(154); - } - auto capacity = capacity_res.unwrap(); - auto bytes_to_write = std::min(len, static_cast(capacity)); - if (!write_to_outgoing_body(borrow, bytes, len)) { - return Result::err(154); + auto bytes_to_write = std::min(bytes_.len - offset_, static_cast(capacity)); + outgoing_body_->write(bytes_.ptr.get() + offset_, bytes_to_write); + offset_ += bytes_to_write; + MOZ_ASSERT(offset_ <= bytes_.len); + if (offset_ == bytes_.len) { + bytes_.ptr.reset(); + RootedObject receiver(engine->cx(), cb_receiver_); + bool result = cb_(engine->cx(), receiver); + cb_ = nullptr; + cb_receiver_ = nullptr; + return result; + } } + } - bytes += bytes_to_write; - len -= bytes_to_write; + [[nodiscard]] bool cancel(api::Engine *engine) override { + MOZ_ASSERT_UNREACHABLE("BodyWriteAllTask's semantics don't allow for cancellation"); + return true; + } + + [[nodiscard]] int32_t id() override { + return outgoing_pollable_; } + void trace(JSTracer *trc) override { + JS::TraceEdge(trc, &cb_receiver_, "BodyWriteAllTask completion callback receiver"); + } +}; + +Result HttpOutgoingBody::write_all(api::Engine *engine, HostBytes bytes, + api::TaskCompletionCallback callback, HandleObject cb_receiver) { + if (!valid()) { + // TODO: proper error handling for all 154 error codes. + return Result::err(154); + } + engine->queue_async_task(new BodyWriteAllTask(this, std::move(bytes), callback, cb_receiver)); return {}; } @@ -638,13 +441,8 @@ class BodyAppendTask final : public api::AsyncTask { HandleObject callback_receiver) : incoming_body_(incoming_body), outgoing_body_(outgoing_body), cb_(completion_callback), cb_receiver_(callback_receiver), state_(State::BlockedOnBoth) { - auto res = incoming_body_->subscribe(); - MOZ_ASSERT(!res.is_err()); - incoming_pollable_ = res.unwrap(); - - res = outgoing_body_->subscribe(); - MOZ_ASSERT(!res.is_err()); - outgoing_pollable_ = res.unwrap(); + incoming_pollable_ = incoming_body_->subscribe().unwrap(); + outgoing_pollable_ = outgoing_body_->subscribe().unwrap(); } [[nodiscard]] bool run(api::Engine *engine) override {