Skip to content

Enable onForeignFunction and proxy-specific extensions to the ABI. #23

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

Merged
merged 13 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

git_repository(
name = "proxy_wasm_cpp_sdk",
commit = "f44562520bca7bfeee77d6284a96d2900f2f13ac",
commit = "35163bbf32fccfbde7b95d909a392dc1dc562596",
remote = "https://github.com/proxy-wasm/proxy-wasm-cpp-sdk",
)

Expand Down
80 changes: 68 additions & 12 deletions include/proxy-wasm/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,46 @@ struct PluginBase {
std::string log_prefix_;
};

struct BufferBase : public BufferInterface {
BufferBase() = default;
~BufferBase() override = default;

// BufferInterface
size_t size() const override {
if (owned_data_) {
return owned_data_size_;
}
return data_.size();
}
WasmResult copyTo(WasmBase *wasm, size_t start, size_t length, uint64_t ptr_ptr,
uint64_t size_ptr) const override;
WasmResult copyFrom(size_t /* start */, size_t /* length */, string_view /* data */) override {
Copy link
Member

Choose a reason for hiding this comment

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

Now that this is independent from Envoy, we should probably stop using string_view to represent bytes, and use span instead.

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'll open an issue for that. I presume that absl::Span is a suitable substitute. #28

// Setting a string buffer not supported (no use case).
return WasmResult::BadArgument;
}

virtual void clear() {
data_ = "";
owned_data_ = nullptr;
}
BufferBase *set(string_view data) {
clear();
data_ = data;
return this;
}
BufferBase *set(std::unique_ptr<char[]> owned_data, uint32_t owned_data_size) {
clear();
owned_data_ = std::move(owned_data);
owned_data_size_ = owned_data_size;
return this;
}

protected:
string_view data_;
std::unique_ptr<char[]> owned_data_;
uint32_t owned_data_size_;
};

/**
* ContextBase is the interface between the VM host and the VM. It has several uses:
*
Expand Down Expand Up @@ -94,7 +134,7 @@ class ContextBase : public RootInterface,
ContextBase(); // Testing.
ContextBase(WasmBase *wasm); // Vm Context.
ContextBase(WasmBase *wasm, std::shared_ptr<PluginBase> plugin); // Root Context.
ContextBase(WasmBase *wasm, uint32_t root_context_id,
ContextBase(WasmBase *wasm, uint32_t parent_context_id,
std::shared_ptr<PluginBase> plugin); // Stream context.
virtual ~ContextBase();

Expand All @@ -103,8 +143,17 @@ class ContextBase : public RootInterface,
// The VM Context used for calling "malloc" has an id_ == 0.
bool isVmContext() const { return id_ == 0; }
// Root Contexts have the VM Context as a parent.
bool isRootContext() const { return root_context_id_ == 0; }
ContextBase *root_context() const { return root_context_; }
bool isRootContext() const { return parent_context_id_ == 0; }
ContextBase *parent_context() const { return parent_context_; }
ContextBase *root_context() const {
const ContextBase *previous = this;
ContextBase *parent = parent_context_;
while (parent != previous) {
previous = parent;
parent = parent->parent_context_;
Copy link
Member

Choose a reason for hiding this comment

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

I think that will result in nullptr dereference when (accidentally) called on the root context itself. Maybe add a check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

parent_context_ is never nullptr. The list is terminated by parent_context_ == this. This is enforced by the constructors for Context.

}
return parent;
}
string_view root_id() const { return isRootContext() ? root_id_ : plugin_->root_id_; }
string_view log_prefix() const {
return isRootContext() ? root_log_prefix_ : plugin_->log_prefix();
Expand All @@ -121,10 +170,11 @@ class ContextBase : public RootInterface,
*/

// Context
void onCreate(uint32_t parent_context_id) override;
void onCreate() override;
bool onDone() override;
void onLog() override;
void onDelete() override;
void onForeignFunction(uint32_t foreign_function_id, uint32_t data_size) override;

// Root
bool onStart(std::shared_ptr<PluginBase> plugin) override;
Expand Down Expand Up @@ -173,10 +223,6 @@ class ContextBase : public RootInterface,
WasmResult log(uint32_t /* level */, string_view /* message */) override {
return unimplemented();
}
WasmResult setTimerPeriod(std::chrono::milliseconds /* period */,
uint32_t * /* timer_token_ptr */) override {
return unimplemented();
}
uint64_t getCurrentTimeNanoseconds() override {
struct timespec tpe;
clock_gettime(CLOCK_REALTIME, &tpe);
Expand All @@ -189,6 +235,7 @@ class ContextBase : public RootInterface,
unimplemented();
return std::make_pair(1, "unimplmemented");
}
WasmResult setTimerPeriod(std::chrono::milliseconds period, uint32_t *timer_token_ptr) override;

// Buffer
BufferInterface *getBuffer(WasmBufferType /* type */) override {
Expand Down Expand Up @@ -316,15 +363,24 @@ class ContextBase : public RootInterface,

WasmBase *wasm_{nullptr};
uint32_t id_{0};
uint32_t root_context_id_{0}; // 0 for roots and the general context.
ContextBase *root_context_{nullptr}; // set in all contexts.
std::string root_id_; // set only in root context.
std::string root_log_prefix_; // set only in root context.
uint32_t parent_context_id_{0}; // 0 for roots and the general context.
ContextBase *parent_context_{nullptr}; // set in all contexts.
std::string root_id_; // set only in root context.
std::string root_log_prefix_; // set only in root context.
std::shared_ptr<PluginBase> plugin_;
bool in_vm_context_created_ = false;
bool destroyed_ = false;
};

class DeferAfterCallActions {
public:
DeferAfterCallActions(ContextBase *context) : wasm_(context->wasm()) {}
~DeferAfterCallActions();

private:
WasmBase *const wasm_;
};

uint32_t resolveQueueForTest(string_view vm_id, string_view queue_name);

} // namespace proxy_wasm
12 changes: 10 additions & 2 deletions include/proxy-wasm/context_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,9 @@ struct RootInterface : public RootGrpcInterface {
/**
* Call on a host Context to create a corresponding Context in the VM. Note:
* onNetworkNewConnection and onRequestHeaders() call onCreate().
* @param parent_context_id is the parent Context id for the context being created. For a
* stream Context this will be a Root Context id (or sub-Context thereof).
*/
virtual void onCreate(uint32_t parent_context_id) = 0;
virtual void onCreate() = 0;

/**
* Call on a Root Context when a VM first starts up.
Expand Down Expand Up @@ -564,6 +563,15 @@ struct GeneralInterface {
* serialized..
*/
virtual WasmResult setProperty(string_view key, string_view value) = 0;

/**
* Custom extension call into the VM. Data is provided as WasmBufferType::CallData.
* @param foreign_function_id a unique identifier for the calling foreign function. These are
* defined and allocated by the foreign function implementor.
* @param data_size is the size of the WasmBufferType::CallData buffer containing data for this
* foreign function call.
*/
virtual void onForeignFunction(uint32_t foreign_function_id, uint32_t data_size) = 0;
};

/**
Expand Down
10 changes: 10 additions & 0 deletions include/proxy-wasm/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
#include "include/proxy-wasm/word.h"

namespace proxy_wasm {

class ContextBase;

extern thread_local ContextBase *current_context_;

namespace exports {

// ABI functions exported from envoy to wasm.
Expand Down Expand Up @@ -107,5 +112,10 @@ void wasi_unstable_proc_exit(void *, Word);
void wasi_unstable_proc_exit(void *, Word);
Word pthread_equal(void *, Word left, Word right);

// Support for embedders, not exported to Wasm.

// Any currently executing Wasm call context.
::proxy_wasm::ContextBase *ContextOrEffectiveContext(::proxy_wasm::ContextBase *context);

} // namespace exports
} // namespace proxy_wasm
14 changes: 8 additions & 6 deletions include/proxy-wasm/null_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,19 @@
#include "google/protobuf/message.h"
#include "include/proxy-wasm/null_vm_plugin.h"
#include "include/proxy-wasm/wasm.h"
#include "include/proxy-wasm/exports.h"

namespace proxy_wasm {
namespace null_plugin {
template <typename T> using Optional = optional<T>;
using StringView = string_view;
#include "proxy_wasm_enums.h"
} // namespace null_plugin
} // namespace proxy_wasm

#include "include/proxy-wasm/wasm_api_impl.h"

namespace proxy_wasm {
namespace null_plugin {
using StringView = string_view;
template <typename T> using Optional = optional<T>;
#include "proxy_wasm_api.h"
} // namespace null_plugin

/**
* Registry for Plugin implementation.
Expand All @@ -48,6 +46,8 @@ struct NullPluginRegistry {
uint32_t (*proxy_on_configure_)(uint32_t root_context_id,
uint32_t plugin_configuration_size) = nullptr;
void (*proxy_on_tick_)(uint32_t context_id) = nullptr;
void (*proxy_on_foreign_function_)(uint32_t context_id, uint32_t token,
uint32_t data_size) = nullptr;
uint32_t (*proxy_on_done_)(uint32_t context_id) = nullptr;
void (*proxy_on_delete_)(uint32_t context_id) = nullptr;
std::unordered_map<std::string, null_plugin::RootFactory> root_factories;
Expand All @@ -74,6 +74,8 @@ class NullPlugin : public NullVmPlugin {
bool onConfigure(uint64_t root_context_id, uint64_t plugin_configuration_size);
void onTick(uint64_t root_context_id);
void onQueueReady(uint64_t root_context_id, uint64_t token);
void onForeignFunction(uint64_t root_context_id, uint64_t foreign_function_id,
uint64_t data_size);

void onCreate(uint64_t context_id, uint64_t root_context_id);

Expand Down Expand Up @@ -110,12 +112,12 @@ class NullPlugin : public NullVmPlugin {

void error(string_view message) { wasm_vm_->error(message); }

private:
null_plugin::Context *ensureContext(uint64_t context_id, uint64_t root_context_id);
null_plugin::RootContext *ensureRootContext(uint64_t context_id);
null_plugin::RootContext *getRootContext(uint64_t context_id);
null_plugin::ContextBase *getContextBase(uint64_t context_id);

private:
NullPluginRegistry *registry_{};
std::unordered_map<std::string, null_plugin::RootContext *> root_context_map_;
std::unordered_map<int64_t, std::unique_ptr<null_plugin::ContextBase>> context_map_;
Expand Down
47 changes: 25 additions & 22 deletions include/proxy-wasm/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
const std::string &vm_configuration() const;
bool allow_precompiled() const { return allow_precompiled_; }

void timerReady(uint32_t root_context_id);
void queueReady(uint32_t root_context_id, uint32_t token);

void startShutdown();
WasmResult done(ContextBase *root_context);
void finishShutdown();

// Proxy specific extension points.
//
virtual void registerCallbacks(); // Register functions called out from Wasm.
virtual void getFunctions(); // Get functions call into Wasm.
virtual CallOnThreadFunction callOnThreadFunction() {
unimplemented();
return nullptr;
Expand All @@ -86,18 +97,17 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
return new ContextBase(this, plugin);
return new ContextBase(this);
}

virtual void setTickPeriod(uint32_t root_context_id, std::chrono::milliseconds tick_period) {
tick_period_[root_context_id] = tick_period;
virtual void setTimerPeriod(uint32_t root_context_id, std::chrono::milliseconds period) {
timer_period_[root_context_id] = period;
}
void tick(uint32_t root_context_id);
void queueReady(uint32_t root_context_id, uint32_t token);

void startShutdown();
WasmResult done(ContextBase *root_context);
void finishShutdown();
virtual void error(string_view message) {
std::cerr << message << "\n";
abort();
}
virtual void unimplemented() { error("unimplemented proxy-wasm API"); }

// Support functions.
//
void *allocMemory(uint64_t size, uint64_t *address);
// Allocate a null-terminated string in the VM and return the pointer to use as a call arguments.
uint64_t copyString(string_view s);
Expand All @@ -107,14 +117,9 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {

WasmForeignFunction getForeignFunction(string_view function_name);

virtual void error(string_view message) {
std::cerr << message << "\n";
abort();
}
virtual void unimplemented() { error("unimplemented proxy-wasm API"); }

// For testing.
void setContext(ContextBase *context) { contexts_[context->id()] = context; }
//
void setContextForTesting(ContextBase *context) { contexts_[context->id()] = context; }
// Returns false if onStart returns false.
bool startForTesting(std::unique_ptr<ContextBase> root_context,
std::shared_ptr<PluginBase> plugin);
Expand Down Expand Up @@ -146,8 +151,6 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
}
}

// These are the same as the values of the MetricType enum, here separately for
// convenience.
static const uint32_t kMetricTypeMask = 0x3; // Enough to cover the 3 types.
static const uint32_t kMetricIdIncrement = 0x4; // Enough to cover the 3 types.
bool isCounterMetricId(uint32_t metric_id) {
Expand All @@ -166,9 +169,8 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
protected:
friend class ContextBase;
class ShutdownHandle;
void registerCallbacks(); // Register functions called out from WASM.

void establishEnvironment(); // Language specific environments.
void getFunctions(); // Get functions call into WASM.

std::string vm_id_; // User-provided vm_id.
std::string vm_key_; // vm_id + hash of code.
Expand All @@ -179,8 +181,8 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
std::shared_ptr<ContextBase> vm_context_; // Context unrelated to any specific root or stream
// (e.g. for global constructors).
std::unordered_map<std::string, std::unique_ptr<ContextBase>> root_contexts_;
std::unordered_map<uint32_t, ContextBase *> contexts_; // Contains all contexts.
std::unordered_map<uint32_t, std::chrono::milliseconds> tick_period_; // per root_id.
std::unordered_map<uint32_t, ContextBase *> contexts_; // Contains all contexts.
std::unordered_map<uint32_t, std::chrono::milliseconds> timer_period_; // per root_id.
std::unique_ptr<ShutdownHandle> shutdown_handle_;
std::unordered_set<ContextBase *> pending_done_; // Root contexts not done during shutdown.

Expand Down Expand Up @@ -224,6 +226,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
WasmCallVoid<3> on_grpc_receive_trailing_metadata_;

WasmCallVoid<2> on_queue_ready_;
WasmCallVoid<3> on_foreign_function_;

WasmCallWord<1> on_done_;
WasmCallVoid<1> on_log_;
Expand Down
14 changes: 4 additions & 10 deletions include/proxy-wasm/wasm_api_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@
#include "include/proxy-wasm/compat.h"

namespace proxy_wasm {
namespace null_plugin {
class RootContext;
class Context;
} // namespace null_plugin

null_plugin::RootContext *nullVmGetRoot(string_view root_id);
null_plugin::Context *nullVmGetContext(uint32_t context_id);

namespace null_plugin {

#define WS(_x) Word(static_cast<uint64_t>(_x))
Expand Down Expand Up @@ -264,8 +256,10 @@ inline WasmResult proxy_call_foreign_function(const char *function_name, size_t
#undef WS
#undef WR

inline RootContext *getRoot(string_view root_id) { return nullVmGetRoot(root_id); }
inline Context *getContext(uint32_t context_id) { return nullVmGetContext(context_id); }
#include "proxy_wasm_api.h"

RootContext *getRoot(string_view root_id);
Context *getContext(uint32_t context_id);

} // namespace null_plugin
} // namespace proxy_wasm
Loading