diff options
Diffstat (limited to 'libdeno')
-rw-r--r-- | libdeno/api.cc | 47 | ||||
-rw-r--r-- | libdeno/binding.cc | 61 | ||||
-rw-r--r-- | libdeno/deno.h | 18 | ||||
-rw-r--r-- | libdeno/internal.h | 31 | ||||
-rw-r--r-- | libdeno/libdeno_test.cc | 63 | ||||
-rw-r--r-- | libdeno/libdeno_test.js | 6 | ||||
-rw-r--r-- | libdeno/modules_test.cc | 4 | ||||
-rw-r--r-- | libdeno/test.cc | 2 | ||||
-rw-r--r-- | libdeno/test.h | 2 |
9 files changed, 149 insertions, 85 deletions
diff --git a/libdeno/api.cc b/libdeno/api.cc index 2601862a2..4cb7b846b 100644 --- a/libdeno/api.cc +++ b/libdeno/api.cc @@ -78,6 +78,19 @@ deno::DenoIsolate* unwrap(Deno* d_) { return reinterpret_cast<deno::DenoIsolate*>(d_); } +void deno_lock(Deno* d_) { + auto* d = unwrap(d_); + CHECK_NULL(d->locker_); + d->locker_ = new v8::Locker(d->isolate_); +} + +void deno_unlock(Deno* d_) { + auto* d = unwrap(d_); + CHECK_NOT_NULL(d->locker_); + delete d->locker_; + d->locker_ = nullptr; +} + deno_buf deno_get_snapshot(Deno* d_) { auto* d = unwrap(d_); CHECK_NOT_NULL(d->snapshot_creator_); @@ -87,7 +100,7 @@ deno_buf deno_get_snapshot(Deno* d_) { auto blob = d->snapshot_creator_->CreateBlob( v8::SnapshotCreator::FunctionCodeHandling::kKeep); return {nullptr, 0, reinterpret_cast<uint8_t*>(const_cast<char*>(blob.data)), - blob.raw_size}; + blob.raw_size, 0}; } static std::unique_ptr<v8::Platform> platform; @@ -127,12 +140,23 @@ void deno_execute(Deno* d_, void* user_data, const char* js_filename, deno::Execute(context, js_filename, js_source); } -void deno_respond(Deno* d_, void* user_data, int32_t req_id, deno_buf buf) { +void deno_zero_copy_release(Deno* d_, size_t zero_copy_id) { + auto* d = unwrap(d_); + v8::Isolate::Scope isolate_scope(d->isolate_); + v8::Locker locker(d->isolate_); + v8::HandleScope handle_scope(d->isolate_); + d->DeleteZeroCopyRef(zero_copy_id); +} + +void deno_respond(Deno* d_, void* user_data, deno_buf buf) { auto* d = unwrap(d_); if (d->current_args_ != nullptr) { // Synchronous response. - auto ab = deno::ImportBuf(d, buf); - d->current_args_->GetReturnValue().Set(ab); + if (buf.data_ptr != nullptr) { + DCHECK_EQ(buf.zero_copy_id, 0); + auto ab = deno::ImportBuf(d, buf); + d->current_args_->GetReturnValue().Set(ab); + } d->current_args_ = nullptr; return; } @@ -148,8 +172,6 @@ void deno_respond(Deno* d_, void* user_data, int32_t req_id, deno_buf buf) { v8::TryCatch try_catch(d->isolate_); - deno::DeleteDataRef(d, req_id); - auto recv_ = d->recv_.Get(d->isolate_); if (recv_.IsEmpty()) { d->last_exception_ = "libdeno.recv_ has not been called."; @@ -157,8 +179,17 @@ void deno_respond(Deno* d_, void* user_data, int32_t req_id, deno_buf buf) { } v8::Local<v8::Value> args[1]; - args[0] = deno::ImportBuf(d, buf); - auto v = recv_->Call(context, context->Global(), 1, args); + int argc = 0; + + // You cannot use zero_copy_buf with deno_respond(). Use + // deno_zero_copy_release() instead. + DCHECK_EQ(buf.zero_copy_id, 0); + if (buf.data_ptr != nullptr) { + args[0] = deno::ImportBuf(d, buf); + argc = 1; + } + + auto v = recv_->Call(context, context->Global(), argc, args); if (try_catch.HasCaught()) { CHECK(v.IsEmpty()); diff --git a/libdeno/binding.cc b/libdeno/binding.cc index f640fe83c..d4849db7b 100644 --- a/libdeno/binding.cc +++ b/libdeno/binding.cc @@ -44,20 +44,6 @@ v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index, return {payload, size}; } -void AddDataRef(DenoIsolate* d, int32_t req_id, v8::Local<v8::Value> data_v) { - d->async_data_map_.emplace(std::piecewise_construct, std::make_tuple(req_id), - std::make_tuple(d->isolate_, data_v)); -} - -void DeleteDataRef(DenoIsolate* d, int32_t req_id) { - // Delete persistent reference to data ArrayBuffer. - auto it = d->async_data_map_.find(req_id); - if (it != d->async_data_map_.end()) { - it->second.Reset(); - d->async_data_map_.erase(it); - } -} - // Extracts a C string from a v8::V8 Utf8Value. const char* ToCString(const v8::String::Utf8Value& value) { return *value ? *value : "<string conversion failed>"; @@ -131,6 +117,13 @@ void ErrorToJSON(const v8::FunctionCallbackInfo<v8::Value>& args) { } v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf) { + // Do not use ImportBuf with zero_copy buffers. + DCHECK_EQ(buf.zero_copy_id, 0); + + if (buf.data_ptr == nullptr) { + return v8::Local<v8::Uint8Array>(); + } + if (buf.alloc_ptr == nullptr) { // If alloc_ptr isn't set, we memcpy. // This is currently used for flatbuffers created in Rust. @@ -209,42 +202,44 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) { DenoIsolate* d = DenoIsolate::FromIsolate(isolate); DCHECK_EQ(d->isolate_, isolate); - v8::Locker locker(d->isolate_); + deno_buf control = {nullptr, 0u, nullptr, 0u, 0u}; + deno_buf zero_copy = {nullptr, 0u, nullptr, 0u, 0u}; + v8::HandleScope handle_scope(isolate); - CHECK_NULL(d->current_args_); // libdeno.send re-entry forbidden. - int32_t req_id = d->next_req_id_++; + if (args.Length() > 0) { + v8::Local<v8::Value> control_v = args[0]; + if (control_v->IsArrayBufferView()) { + control = + GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(control_v)); + } + } - v8::Local<v8::Value> control_v = args[0]; - CHECK(control_v->IsArrayBufferView()); - deno_buf control = - GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(control_v)); - deno_buf data = {nullptr, 0u, nullptr, 0u}; - v8::Local<v8::Value> data_v; + v8::Local<v8::Value> zero_copy_v; if (args.Length() == 2) { if (args[1]->IsArrayBufferView()) { - data_v = args[1]; - data = GetContents(isolate, v8::Local<v8::ArrayBufferView>::Cast(data_v)); + zero_copy_v = args[1]; + zero_copy = GetContents( + isolate, v8::Local<v8::ArrayBufferView>::Cast(zero_copy_v)); + size_t zero_copy_id = d->next_zero_copy_id_++; + DCHECK_GT(zero_copy_id, 0); + zero_copy.zero_copy_id = zero_copy_id; + // If the zero_copy ArrayBuffer was given, we must maintain a strong + // reference to it until deno_zero_copy_release is called. + d->AddZeroCopyRef(zero_copy_id, zero_copy_v); } - } else { - CHECK_EQ(args.Length(), 1); } DCHECK_NULL(d->current_args_); d->current_args_ = &args; - d->recv_cb_(d->user_data_, req_id, control, data); + d->recv_cb_(d->user_data_, control, zero_copy); if (d->current_args_ == nullptr) { // This indicates that deno_repond() was called already. } else { // Asynchronous. d->current_args_ = nullptr; - // If the data ArrayBuffer was given, we must maintain a strong reference - // to it until deno_respond is called. - if (!data_v.IsEmpty()) { - AddDataRef(d, req_id, data_v); - } } } diff --git a/libdeno/deno.h b/libdeno/deno.h index 6be0b5625..f3902985e 100644 --- a/libdeno/deno.h +++ b/libdeno/deno.h @@ -15,6 +15,7 @@ typedef struct { size_t alloc_len; // Length of the memory allocation. uint8_t* data_ptr; // Start of logical contents (within the allocation). size_t data_len; // Length of logical contents. + size_t zero_copy_id; // 0 = normal, 1 = must call deno_zero_copy_release. } deno_buf; typedef struct deno_s Deno; @@ -22,8 +23,8 @@ typedef struct deno_s Deno; // A callback to receive a message from a libdeno.send() javascript call. // control_buf is valid for only for the lifetime of this callback. // data_buf is valid until deno_respond() is called. -typedef void (*deno_recv_cb)(void* user_data, int32_t req_id, - deno_buf control_buf, deno_buf data_buf); +typedef void (*deno_recv_cb)(void* user_data, deno_buf control_buf, + deno_buf zerop_copy_buf); void deno_init(); const char* deno_v8_version(); @@ -47,6 +48,9 @@ deno_buf deno_get_snapshot(Deno* d); void deno_delete(Deno* d); +void deno_lock(Deno* d); +void deno_unlock(Deno* d); + // Compile and execute a traditional JavaScript script that does not use // module import statements. // If it succeeded deno_last_exception() will return NULL. @@ -66,11 +70,13 @@ void deno_execute(Deno* d, void* user_data, const char* js_filename, // longer owns `buf` and must not use it; deno_respond() is responsible for // releasing its memory.) // -// Calling this function more than once with the same req_id will result in -// an error. -// // If a JS exception was encountered, deno_last_exception() will be non-NULL. -void deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf); +void deno_respond(Deno* d, void* user_data, deno_buf buf); + +// consumes zero_copy +// Calling this function more than once with the same zero_copy_id will result +// in an error. +void deno_zero_copy_release(Deno* d, size_t zero_copy_id); void deno_check_promise_errors(Deno* d); diff --git a/libdeno/internal.h b/libdeno/internal.h index 720966407..563043085 100644 --- a/libdeno/internal.h +++ b/libdeno/internal.h @@ -30,12 +30,13 @@ class DenoIsolate { public: explicit DenoIsolate(deno_config config) : isolate_(nullptr), + locker_(nullptr), shared_(config.shared), current_args_(nullptr), snapshot_creator_(nullptr), global_import_buf_ptr_(nullptr), recv_cb_(config.recv_cb), - next_req_id_(0), + next_zero_copy_id_(1), // zero_copy_id must not be zero. user_data_(nullptr), resolve_cb_(nullptr) { array_buffer_allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); @@ -48,6 +49,9 @@ class DenoIsolate { ~DenoIsolate() { shared_ab_.Reset(); + if (locker_) { + delete locker_; + } if (snapshot_creator_) { delete snapshot_creator_; } else { @@ -78,14 +82,31 @@ class DenoIsolate { } } + void DeleteZeroCopyRef(size_t zero_copy_id) { + DCHECK_NE(zero_copy_id, 0); + // Delete persistent reference to data ArrayBuffer. + auto it = zero_copy_map_.find(zero_copy_id); + if (it != zero_copy_map_.end()) { + it->second.Reset(); + zero_copy_map_.erase(it); + } + } + + void AddZeroCopyRef(size_t zero_copy_id, v8::Local<v8::Value> zero_copy_v) { + zero_copy_map_.emplace(std::piecewise_construct, + std::make_tuple(zero_copy_id), + std::make_tuple(isolate_, zero_copy_v)); + } + v8::Isolate* isolate_; + v8::Locker* locker_; v8::ArrayBuffer::Allocator* array_buffer_allocator_; deno_buf shared_; const v8::FunctionCallbackInfo<v8::Value>* current_args_; v8::SnapshotCreator* snapshot_creator_; void* global_import_buf_ptr_; deno_recv_cb recv_cb_; - int32_t next_req_id_; + size_t next_zero_copy_id_; void* user_data_; v8::Persistent<v8::Object> builtin_modules_; @@ -94,7 +115,7 @@ class DenoIsolate { deno_resolve_cb resolve_cb_; v8::Persistent<v8::Context> context_; - std::map<int32_t, v8::Persistent<v8::Value>> async_data_map_; + std::map<size_t, v8::Persistent<v8::Value>> zero_copy_map_; std::map<int, v8::Persistent<v8::Value>> pending_promise_map_; std::string last_exception_; v8::Persistent<v8::Function> recv_; @@ -152,7 +173,7 @@ static intptr_t external_references[] = { reinterpret_cast<intptr_t>(MessageCallback), 0}; -static const deno_buf empty_buf = {nullptr, 0, nullptr, 0}; +static const deno_buf empty_buf = {nullptr, 0, nullptr, 0, 0}; Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb); @@ -166,8 +187,6 @@ v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index, v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf); -void DeleteDataRef(DenoIsolate* d, int32_t req_id); - bool Execute(v8::Local<v8::Context> context, const char* js_filename, const char* js_source); bool ExecuteMod(v8::Local<v8::Context> context, const char* js_filename, diff --git a/libdeno/libdeno_test.cc b/libdeno/libdeno_test.cc index 3193e7677..90fceef73 100644 --- a/libdeno/libdeno_test.cc +++ b/libdeno/libdeno_test.cc @@ -26,9 +26,11 @@ TEST(LibDenoTest, Snapshotter) { TEST(LibDenoTest, CanCallFunction) { Deno* d = deno_new(deno_config{0, snapshot, empty, nullptr}); + deno_lock(d); deno_execute(d, nullptr, "a.js", "if (CanCallFunction() != 'foo') throw Error();"); EXPECT_EQ(nullptr, deno_last_exception(d)); + deno_unlock(d); deno_delete(d); } @@ -47,6 +49,7 @@ deno_buf strbuf(const char* str) { buf.alloc_len = len + 1; buf.data_ptr = buf.alloc_ptr; buf.data_len = len; + buf.zero_copy_id = 0; return buf; } @@ -71,8 +74,8 @@ void assert_null(deno_buf b) { TEST(LibDenoTest, RecvReturnEmpty) { static int count = 0; - auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) { - assert_null(data_buf); + auto recv_cb = [](auto _, auto buf, auto zero_copy_buf) { + assert_null(zero_copy_buf); count++; EXPECT_EQ(static_cast<size_t>(3), buf.data_len); EXPECT_EQ(buf.data_ptr[0], 'a'); @@ -88,15 +91,17 @@ TEST(LibDenoTest, RecvReturnEmpty) { TEST(LibDenoTest, RecvReturnBar) { static int count = 0; - auto recv_cb = [](auto user_data, int req_id, auto buf, auto data_buf) { + auto recv_cb = [](auto user_data, auto buf, auto zero_copy_buf) { auto d = reinterpret_cast<Deno*>(user_data); - assert_null(data_buf); + assert_null(zero_copy_buf); count++; EXPECT_EQ(static_cast<size_t>(3), buf.data_len); EXPECT_EQ(buf.data_ptr[0], 'a'); EXPECT_EQ(buf.data_ptr[1], 'b'); EXPECT_EQ(buf.data_ptr[2], 'c'); - deno_respond(d, user_data, req_id, strbuf("bar")); + EXPECT_EQ(zero_copy_buf.zero_copy_id, 0u); + EXPECT_EQ(zero_copy_buf.data_ptr, nullptr); + deno_respond(d, user_data, strbuf("bar")); }; Deno* d = deno_new(deno_config{0, snapshot, empty, recv_cb}); deno_execute(d, d, "a.js", "RecvReturnBar()"); @@ -114,9 +119,9 @@ TEST(LibDenoTest, DoubleRecvFails) { TEST(LibDenoTest, SendRecvSlice) { static int count = 0; - auto recv_cb = [](auto user_data, int req_id, auto buf, auto data_buf) { + auto recv_cb = [](auto user_data, auto buf, auto zero_copy_buf) { auto d = reinterpret_cast<Deno*>(user_data); - assert_null(data_buf); + assert_null(zero_copy_buf); static const size_t alloc_len = 1024; size_t i = count++; // Check the size and offset of the slice. @@ -134,12 +139,12 @@ TEST(LibDenoTest, SendRecvSlice) { memcpy(alloc_ptr, buf.alloc_ptr, alloc_len); // Make a slice that is a bit shorter than the original. deno_buf buf2{alloc_ptr, alloc_len, alloc_ptr + data_offset, - buf.data_len - 19}; + buf.data_len - 19, 0}; // Place some values into the buffer for the JS side to verify. buf2.data_ptr[0] = 200 + i; buf2.data_ptr[buf2.data_len - 1] = 200 - i; // Send back. - deno_respond(d, user_data, req_id, buf2); + deno_respond(d, user_data, buf2); }; Deno* d = deno_new(deno_config{0, snapshot, empty, recv_cb}); deno_execute(d, d, "a.js", "SendRecvSlice()"); @@ -150,8 +155,8 @@ TEST(LibDenoTest, SendRecvSlice) { TEST(LibDenoTest, JSSendArrayBufferViewTypes) { static int count = 0; - auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) { - assert_null(data_buf); + auto recv_cb = [](auto _, auto buf, auto zero_copy_buf) { + assert_null(zero_copy_buf); count++; size_t data_offset = buf.data_ptr - buf.alloc_ptr; EXPECT_EQ(data_offset, 2468u); @@ -197,33 +202,39 @@ TEST(LibDenoTest, GlobalErrorHandling) { deno_delete(d); } -TEST(LibDenoTest, DataBuf) { +TEST(LibDenoTest, ZeroCopyBuf) { static int count = 0; - static deno_buf data_buf_copy; - auto recv_cb = [](auto _, int req_id, deno_buf buf, deno_buf data_buf) { + static deno_buf zero_copy_buf2; + auto recv_cb = [](auto user_data, deno_buf buf, deno_buf zero_copy_buf) { count++; - data_buf.data_ptr[0] = 4; - data_buf.data_ptr[1] = 2; - data_buf_copy = data_buf; + EXPECT_GT(zero_copy_buf.zero_copy_id, 0u); + zero_copy_buf.data_ptr[0] = 4; + zero_copy_buf.data_ptr[1] = 2; + zero_copy_buf2 = zero_copy_buf; EXPECT_EQ(2u, buf.data_len); - EXPECT_EQ(2u, data_buf.data_len); + EXPECT_EQ(2u, zero_copy_buf.data_len); EXPECT_EQ(buf.data_ptr[0], 1); EXPECT_EQ(buf.data_ptr[1], 2); + // Note zero_copy_buf won't actually be freed here because in + // libdeno_test.js zeroCopyBuf is a rooted global. We just want to exercise + // the API here. + auto d = reinterpret_cast<Deno*>(user_data); + deno_zero_copy_release(d, zero_copy_buf.zero_copy_id); }; Deno* d = deno_new(deno_config{0, snapshot, empty, recv_cb}); - deno_execute(d, nullptr, "a.js", "DataBuf()"); + deno_execute(d, d, "a.js", "ZeroCopyBuf()"); EXPECT_EQ(nullptr, deno_last_exception(d)); EXPECT_EQ(count, 1); - // data_buf was subsequently changed in JS, let's check that our copy reflects - // that. - EXPECT_EQ(data_buf_copy.data_ptr[0], 9); - EXPECT_EQ(data_buf_copy.data_ptr[1], 8); + // zero_copy_buf was subsequently changed in JS, let's check that our copy + // reflects that. + EXPECT_EQ(zero_copy_buf2.data_ptr[0], 9); + EXPECT_EQ(zero_copy_buf2.data_ptr[1], 8); deno_delete(d); } TEST(LibDenoTest, CheckPromiseErrors) { static int count = 0; - auto recv_cb = [](auto _, int req_id, auto buf, auto data_buf) { count++; }; + auto recv_cb = [](auto _, auto buf, auto zero_copy_buf) { count++; }; Deno* d = deno_new(deno_config{0, snapshot, empty, recv_cb}); EXPECT_EQ(deno_last_exception(d), nullptr); deno_execute(d, nullptr, "a.js", "CheckPromiseErrors()"); @@ -271,7 +282,7 @@ TEST(LibDenoTest, EncodeErrorBug) { TEST(LibDenoTest, Shared) { uint8_t s[] = {0, 1, 2}; - deno_buf shared = {nullptr, 0, s, 3}; + deno_buf shared = {nullptr, 0, s, 3, 0}; Deno* d = deno_new(deno_config{0, snapshot, shared, nullptr}); deno_execute(d, nullptr, "a.js", "Shared()"); EXPECT_EQ(nullptr, deno_last_exception(d)); @@ -306,7 +317,7 @@ TEST(LibDenoTest, LibDenoEvalContextError) { TEST(LibDenoTest, SharedAtomics) { int32_t s[] = {0, 1, 2}; - deno_buf shared = {nullptr, 0, reinterpret_cast<uint8_t*>(s), sizeof s}; + deno_buf shared = {nullptr, 0, reinterpret_cast<uint8_t*>(s), sizeof s, 0}; Deno* d = deno_new(deno_config{0, empty, shared, nullptr}); deno_execute(d, nullptr, "a.js", "Atomics.add(new Int32Array(libdeno.shared), 0, 1)"); diff --git a/libdeno/libdeno_test.js b/libdeno/libdeno_test.js index d6ea5f983..8b1ad2e04 100644 --- a/libdeno/libdeno_test.js +++ b/libdeno/libdeno_test.js @@ -103,11 +103,11 @@ global.GlobalErrorHandling = () => { }; // Allocate this buf at the top level to avoid GC. -const dataBuf = new Uint8Array([3, 4]); +const zeroCopyBuf = new Uint8Array([3, 4]); -global.DataBuf = () => { +global.ZeroCopyBuf = () => { const a = new Uint8Array([1, 2]); - const b = dataBuf; + const b = zeroCopyBuf; // The second parameter of send should modified by the // privileged side. const r = libdeno.send(a, b); diff --git a/libdeno/modules_test.cc b/libdeno/modules_test.cc index d41c38b8e..cb800e89a 100644 --- a/libdeno/modules_test.cc +++ b/libdeno/modules_test.cc @@ -2,10 +2,12 @@ #include "test.h" static int exec_count = 0; -void recv_cb(void* user_data, int req_id, deno_buf buf, deno_buf data_buf) { +void recv_cb(void* user_data, deno_buf buf, deno_buf zero_copy_buf) { // We use this to check that scripts have executed. EXPECT_EQ(1u, buf.data_len); EXPECT_EQ(buf.data_ptr[0], 4); + EXPECT_EQ(zero_copy_buf.zero_copy_id, 0u); + EXPECT_EQ(zero_copy_buf.data_ptr, nullptr); exec_count++; } diff --git a/libdeno/test.cc b/libdeno/test.cc index a8fcbc63b..1340fe8c3 100644 --- a/libdeno/test.cc +++ b/libdeno/test.cc @@ -3,7 +3,7 @@ #include <string> #include "file_util.h" -deno_buf snapshot = {nullptr, 0, nullptr, 0}; +deno_buf snapshot = {nullptr, 0, nullptr, 0, 0}; int main(int argc, char** argv) { // Locate the snapshot. diff --git a/libdeno/test.h b/libdeno/test.h index 25ca93988..2f7c32384 100644 --- a/libdeno/test.h +++ b/libdeno/test.h @@ -6,6 +6,6 @@ #include "testing/gtest/include/gtest/gtest.h" extern deno_buf snapshot; // Loaded in libdeno/test.cc -const deno_buf empty = {nullptr, 0, nullptr, 0}; +const deno_buf empty = {nullptr, 0, nullptr, 0, 0}; #endif // TEST_H_ |