Skip to content

Commit

Permalink
Simplify representation of host references and functions (#25)
Browse files Browse the repository at this point in the history
* Eliminates RefData structs and its subtypes, avoid storing any extra data.
* Implement all reflection methods directly.
* Store host info via weak map.
* Store host function data as host data.
* Use foreign pointer instead of object template with internal slot for function data.
* Link back from isolate to store.
* Add Val::is_num and Val::is_ref short-hands.
* Fix a couple of memory leaks in threads example and simplify it; add to Makefile.
  • Loading branch information
rossberg committed Jul 3, 2018
1 parent 3a103e8 commit 0eab50a
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 359 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ WASM_DIR = .
EXAMPLE_DIR = example

EXAMPLE_OUT = ${OUT_DIR}/${EXAMPLE_DIR}
EXAMPLES = hello callback trap reflect global table memory
EXAMPLES = hello callback trap reflect global table memory threads

V8_VERSION = branch-heads/6.8
V8_ARCH = x64
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ V8 implementation:

### TODO

Tests:

* Create C versions of global, table, memory test.

* Create C++ version of threads test.

V8 implementation:

* Implement Module::serialize, Module::deserialize

* Simplify reference wrappers to be plain persistent handles

* Use reference counting and caching for types

* Add a memory debug mode?
Expand Down
164 changes: 73 additions & 91 deletions example/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,124 +2,110 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "wasm.h"

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#include "wasm.h"

#define own

const int N_THREADS = 10;
const int N_REPS = 3;

// A function to be called from Wasm code.
void hello_callback(const wasm_val_vec_t *args, own wasm_result_t *result) {
wasm_val_t val = args->data[0];
switch (val.kind) {
case WASM_I32: {
printf("> hello from thread: %d\n", val.of.i32);
} break;
default: { printf("> unexpected value kind"); }
}
void callback(const wasm_val_vec_t* args, own wasm_result_t* result) {
assert(args->data[0].kind == WASM_I32);
printf("> Thread %d running\n", args->data[0].of.i32);
wasm_result_new_empty(result);
}

int run(wasm_store_t *store, wasm_module_t *module, int thread_num) {
own wasm_functype_t *hello_type =
wasm_functype_new_1_0(wasm_valtype_new_i32());
own wasm_func_t *hello_func =
wasm_func_new(store, hello_type, hello_callback);
wasm_functype_delete(hello_type);

wasm_val_t hello_global_val = {.kind = WASM_I32};
hello_global_val.of.i32 = thread_num;

// Instantiate.
wasm_extern_t *externs[] = {
// func
wasm_func_as_extern(hello_func),
// global
wasm_global_as_extern(wasm_global_new(
store, wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST),
&hello_global_val)),
};
wasm_extern_vec_t imports = {2, externs};
own wasm_instance_t *instance = wasm_instance_new(store, module, &imports);
if (!instance) {
printf("> Error instantiating module!\n");
return 1;
}

// Extract export.
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return 1;
}
const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return 1;
}

wasm_instance_delete(instance);

// Call.
wasm_val_vec_t args = {0, NULL};
own wasm_result_t result;
wasm_func_call(run_func, &args, &result);
if (result.kind != WASM_RETURN) {
printf("> Error calling function!\n");
return 1;
}
typedef struct {
wasm_engine_t* engine;
wasm_byte_vec_t binary;
int id;
} thread_args;

wasm_extern_vec_delete(&exports);
wasm_result_delete(&result);
void* run(void* args_abs) {
thread_args* args = (thread_args*)args_abs;

return 0;
}
// Create store.
wasm_store_t* store = wasm_store_new(args->engine);

int runExample(wasm_store_t *store, wasm_byte_vec_t binary, int thread_num) {
// Compile.
own wasm_module_t *module = wasm_module_new(store, &binary);
own wasm_module_t* module = wasm_module_new(store, &args->binary);
if (!module) {
printf("> Error compiling module!\n");
return 1;
return NULL;
}

// Run the example N times.
for (int i = 0; i < N_REPS; ++i) {
usleep(1e5);
run(store, module, thread_num);
usleep(100000);

// Create imports.
own wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i32());
own wasm_func_t* func = wasm_func_new(store, func_type, callback);
wasm_functype_delete(func_type);

wasm_val_t val = {.kind = WASM_I32, .of = {.i32 = (int32_t)args->id}};
own wasm_globaltype_t* global_type =
wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST);
own wasm_global_t* global = wasm_global_new(store, global_type, &val);
wasm_globaltype_delete(global_type);

// Instantiate.
wasm_extern_t* externs[] = {
wasm_func_as_extern(func),
wasm_global_as_extern(global),
};
wasm_extern_vec_t imports = {2, externs};
own wasm_instance_t* instance = wasm_instance_new(store, module, &imports);
if (!instance) {
printf("> Error instantiating module!\n");
return NULL;
}

// Extract export.
own wasm_extern_vec_t exports;
wasm_instance_exports(instance, &exports);
if (exports.size == 0) {
printf("> Error accessing exports!\n");
return NULL;
}
const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]);
if (run_func == NULL) {
printf("> Error accessing export!\n");
return NULL;
}

wasm_instance_delete(instance);

// Call.
wasm_val_vec_t args = {0, NULL};
own wasm_result_t result;
wasm_func_call(run_func, &args, &result);
if (result.kind != WASM_RETURN) {
printf("> Error calling function!\n");
return NULL;
}

wasm_extern_vec_delete(&exports);
wasm_result_delete(&result);
}

wasm_module_delete(module);
return 0;
}

typedef struct thread_args {
wasm_engine_t *engine;
wasm_byte_vec_t binary;
int id;
} thread_args;

void *threadStart(void *args0) {
thread_args *args = (thread_args *)args0;
wasm_store_t *store = wasm_store_new(args->engine);
runExample(store, args->binary, args->id);
wasm_store_delete(store);

return NULL;
}

int main(int argc, const char *argv[]) {
// Initialize.
wasm_engine_t *engine = wasm_engine_new(argc, argv);
wasm_engine_t* engine = wasm_engine_new(argc, argv);

// Load binary.
FILE *file = fopen("threads.wasm", "r");
FILE* file = fopen("threads.wasm", "r");
if (!file) {
printf("> Error loading module!\n");
return 1;
Expand All @@ -134,24 +120,20 @@ int main(int argc, const char *argv[]) {

pthread_t threads[N_THREADS];
for (int i = 0; i < N_THREADS; i++) {
pthread_t pth;
thread_args *args = malloc(sizeof(thread_args));
thread_args* args = malloc(sizeof(thread_args));
args->id = i;
args->engine = engine;
args->binary = binary;
printf("Initializing thread %d...\n", i);
pthread_create(&pth, NULL, threadStart, args);
threads[i] = pth;
pthread_create(&threads[i], NULL, &run, args);
}

for (int i = 0; i < N_THREADS; i++) {
printf("Waiting for thread: %d\n", i);
pthread_t pth = threads[i];
pthread_join(pth, NULL);
pthread_join(threads[i], NULL);
}

wasm_byte_vec_delete(&binary);

wasm_engine_delete(engine);

return 0;
Expand Down
9 changes: 9 additions & 0 deletions example/threads.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <iostream>

#include "wasm.hh"

int main(int argc, const char* argv[]) {
// TODO
std::cout << "Done." << std::endl;
return 0;
}
Binary file modified example/threads.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions example/threads.wat
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(module
(func $hello (import "" "hello") (param i32))
(func $message (import "" "hello") (param i32))
(global $id (import "" "id") i32)
(func (export "run") (call $hello (get_global $id)))
(func (export "run") (call $message (get_global $id)))
)
15 changes: 9 additions & 6 deletions include/wasm.hh
Original file line number Diff line number Diff line change
Expand Up @@ -460,13 +460,16 @@ public:
Val(own<Ref*>&& r) : kind_(ANYREF) { impl_.ref = r.release(); }

Val(Val&& that) : kind_(that.kind_), impl_(that.impl_) {
if (is_ref(kind_)) that.impl_.ref = nullptr;
if (is_ref()) that.impl_.ref = nullptr;
}

~Val() {
reset();
}

auto is_num() const -> bool { return wasm::is_num(kind_); }
auto is_ref() const -> bool { return wasm::is_ref(kind_); }

static auto i32(int32_t x) -> Val { return Val(x); }
static auto i64(int64_t x) -> Val { return Val(x); }
static auto f32(float32_t x) -> Val { return Val(x); }
Expand All @@ -476,7 +479,7 @@ public:
template<class T> inline static auto make(own<T>&& x) -> Val;

void reset() {
if (is_ref(kind_) && impl_.ref) {
if (is_ref() && impl_.ref) {
delete impl_.ref;
impl_.ref = nullptr;
}
Expand All @@ -486,7 +489,7 @@ public:
reset();
kind_ = that.kind_;
impl_ = that.impl_;
if (is_ref(kind_)) that.impl_.ref = nullptr;
if (is_ref()) that.impl_.ref = nullptr;
}

auto operator=(Val&& that) -> Val& {
Expand All @@ -499,18 +502,18 @@ public:
auto i64() const -> int64_t { assert(kind_ == I64); return impl_.i64; }
auto f32() const -> float32_t { assert(kind_ == F32); return impl_.f32; }
auto f64() const -> float64_t { assert(kind_ == F64); return impl_.f64; }
auto ref() const -> Ref* { assert(is_ref(kind_)); return impl_.ref; }
auto ref() const -> Ref* { assert(is_ref()); return impl_.ref; }
template<class T> inline auto get() const -> T;

auto release_ref() -> own<Ref*> {
assert(is_ref(kind_));
assert(is_ref());
auto ref = impl_.ref;
ref = nullptr;
return own<Ref*>(ref);
}

auto copy() const -> Val {
if (is_ref(kind_) && impl_.ref != nullptr) {
if (is_ref() && impl_.ref != nullptr) {
impl impl = {.ref = impl_.ref->copy().release()};
return Val(kind_, impl);
} else {
Expand Down
6 changes: 1 addition & 5 deletions src/wasm-c.cc
Original file line number Diff line number Diff line change
Expand Up @@ -620,11 +620,7 @@ struct borrowed_val {
Val it;
borrowed_val(Val&& v) : it(std::move(v)) {}
borrowed_val(borrowed_val&& that) : it(std::move(that.it)) {}
~borrowed_val() {
if(is_ref(it.kind())) {
it.release_ref();
}
}
~borrowed_val() { if (it.is_ref()) it.release_ref(); }
};

inline auto borrow(const wasm_val_t* v) -> borrowed_val {
Expand Down
25 changes: 24 additions & 1 deletion src/wasm-v8-lowlevel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
namespace v8 {
namespace wasm {

// Handles

auto object_isolate(v8::Handle<v8::Object> obj) -> v8::Isolate* {
auto v8_obj = v8::Utils::OpenHandle(*obj);
return reinterpret_cast<v8::Isolate*>(v8_obj->GetIsolate());
}

auto object_isolate(const v8::Persistent<v8::Object>& obj) -> v8::Isolate* {
struct FakePersistent { v8::Object* val; };
auto v8_obj = reinterpret_cast<const FakePersistent*>(&obj)->val;
return v8_obj->GetIsolate();
}


// Foreign pointers

auto foreign_new(v8::Isolate* isolate, void* ptr) -> v8::Local<v8::Value> {
Expand All @@ -30,7 +44,9 @@ auto foreign_new(v8::Isolate* isolate, void* ptr) -> v8::Local<v8::Value> {
}

auto foreign_get(v8::Local<v8::Value> val) -> void* {
auto addr = v8::ToCData<v8::internal::Address>(*v8::Utils::OpenHandle(*val));
auto foreign = v8::Utils::OpenHandle(*val);
if (!foreign->IsForeign()) return nullptr;
auto addr = v8::ToCData<v8::internal::Address>(*foreign);
return reinterpret_cast<void*>(addr);
}

Expand Down Expand Up @@ -137,6 +153,13 @@ auto module_binary(v8::Local<v8::Object> module) -> const char* {

// Instance

auto instance_module(v8::Local<v8::Object> instance) -> v8::Local<v8::Object> {
auto v8_object = v8::Utils::OpenHandle<v8::Object, v8::internal::JSReceiver>(instance);
auto v8_instance = v8::internal::Handle<v8::internal::WasmInstanceObject>::cast(v8_object);
auto v8_module = handle(v8::internal::JSObject::cast(v8_instance->module_object()));
return v8::Utils::ToLocal(v8_module);
}

auto instance_exports(v8::Local<v8::Object> instance) -> v8::Local<v8::Object> {
auto v8_object = v8::Utils::OpenHandle<v8::Object, v8::internal::JSReceiver>(instance);
auto v8_instance = v8::internal::Handle<v8::internal::WasmInstanceObject>::cast(v8_object);
Expand Down
Loading

0 comments on commit 0eab50a

Please sign in to comment.