Skip to content

Commit

Permalink
Reorganize WASI 0.2.0 bindings to facilitate reuse (#175)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
tschneidereit authored Nov 6, 2024
1 parent aa18177 commit 8dc4801
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 241 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
254 changes: 254 additions & 0 deletions host-apis/wasi-0.2.0/handles.h
Original file line number Diff line number Diff line change
@@ -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 <exports.h>
#include <list>
#ifdef DEBUG
#include <set>
#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 <class T> struct HandleOps {};

template <class T> class WASIHandle : public host_api::HandleState {
#ifdef DEBUG
static inline auto used_handles = std::set<Handle>();
#endif

protected:
Handle handle_;
#ifdef DEBUG
bool owned_;
#endif

public:
using Borrowed = typename HandleOps<T>::borrowed;

explicit WASIHandle(typename HandleOps<T>::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<T>::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<T> *cast(HandleState *handle) {
return reinterpret_cast<WASIHandle<T> *>(handle);
}

typename HandleOps<T>::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<T>::borrowed borrow() const {
MOZ_ASSERT(valid());
LOG_HANDLE_OP("borrowing handle %d\n", handle_);
return {handle_};
}

typename HandleOps<T>::owned take() {
MOZ_ASSERT(valid());
MOZ_ASSERT(owned_);
LOG_HANDLE_OP("taking handle %d\n", handle_);
typename HandleOps<T>::owned handle = {handle_};
#ifdef DEBUG
used_handles.erase(handle_);
#endif
handle_ = POISONED_HANDLE;
return handle;
}
};

template <class T> struct Borrow {
static constexpr typename HandleOps<T>::borrowed invalid{std::numeric_limits<int32_t>::max()};
typename HandleOps<T>::borrowed handle_{invalid};

explicit Borrow(host_api::HandleState *handle) {
handle_ = WASIHandle<T>::cast(handle)->borrow();
}

explicit Borrow(typename HandleOps<T>::borrowed handle) { handle_ = handle; }

explicit Borrow(typename HandleOps<T>::owned handle) { handle_ = {handle.__handle}; }

operator typename HandleOps<T>::borrowed() const { return handle_; }
};

template <> struct HandleOps<host_api::Pollable> {
using owned = wasi_io_poll_own_pollable_t;
using borrowed = wasi_io_poll_borrow_pollable_t;
};

template <> struct HandleOps<host_api::HttpHeaders> {
using owned = wasi_http_types_own_headers_t;
using borrowed = wasi_http_types_borrow_fields_t;
};

template <> struct HandleOps<host_api::HttpIncomingRequest> {
using owned = wasi_http_types_own_incoming_request_t;
using borrowed = wasi_http_types_borrow_incoming_request_t;
};

template <> struct HandleOps<host_api::HttpOutgoingRequest> {
using owned = wasi_http_types_own_outgoing_request_t;
using borrowed = wasi_http_types_borrow_outgoing_request_t;
};

template <> struct HandleOps<host_api::FutureHttpIncomingResponse> {
using owned = wasi_http_types_own_future_incoming_response_t;
using borrowed = wasi_http_types_borrow_future_incoming_response_t;
};

template <> struct HandleOps<host_api::HttpIncomingResponse> {
using owned = wasi_http_types_own_incoming_response_t;
using borrowed = wasi_http_types_borrow_incoming_response_t;
};

template <> struct HandleOps<host_api::HttpOutgoingResponse> {
using owned = wasi_http_types_own_outgoing_response_t;
using borrowed = wasi_http_types_borrow_outgoing_response_t;
};

template <> struct HandleOps<host_api::HttpIncomingBody> {
using owned = wasi_http_types_own_incoming_body_t;
using borrowed = wasi_http_types_borrow_incoming_body_t;
};

template <> struct HandleOps<host_api::HttpOutgoingBody> {
using owned = wasi_http_types_own_outgoing_body_t;
using borrowed = wasi_http_types_borrow_outgoing_body_t;
};

struct OutputStream {};
template <> struct HandleOps<OutputStream> {
using owned = wasi_io_streams_own_output_stream_t;
using borrowed = wasi_io_streams_borrow_output_stream_t;
};

struct InputStream {};
template <> struct HandleOps<InputStream> {
using owned = wasi_io_streams_own_input_stream_t;
using borrowed = wasi_io_streams_borrow_input_stream_t;
};

class IncomingBodyHandle final : public WASIHandle<host_api::HttpIncomingBody> {
HandleOps<InputStream>::owned stream_handle_;
PollableHandle pollable_handle_;

friend host_api::HttpIncomingBody;
friend host_api::HttpOutgoingBody;

public:
explicit IncomingBodyHandle(HandleOps<host_api::HttpIncomingBody>::owned handle)
: WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) {
HandleOps<InputStream>::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<IncomingBodyHandle *>(handle);
}
};

class OutgoingBodyHandle final : public WASIHandle<host_api::HttpOutgoingBody> {
HandleOps<OutputStream>::owned stream_handle_;
PollableHandle pollable_handle_;

friend host_api::HttpOutgoingBody;

public:
explicit OutgoingBodyHandle(HandleOps<host_api::HttpOutgoingBody>::owned handle)
: WASIHandle(handle), pollable_handle_(INVALID_POLLABLE_HANDLE) {
HandleOps<OutputStream>::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<OutgoingBodyHandle *>(handle);
}
};

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading

0 comments on commit 8dc4801

Please sign in to comment.