diff options
author | Bert Belder <bertbelder@gmail.com> | 2019-04-28 21:31:10 +0200 |
---|---|---|
committer | Bert Belder <bertbelder@gmail.com> | 2019-05-01 21:11:09 +0200 |
commit | 41c7e96f1a81ea416ebb3ba45f2815e0202d6b75 (patch) | |
tree | 5fcb15b8664d7579cf4db76d16754c8efa7c4667 /core/libdeno/binding.cc | |
parent | abdb98a2516a9d6ec313805dffbc2107d38f8ed4 (diff) |
Refactor zero-copy buffers for performance and to prevent memory leaks
* In order to prevent ArrayBuffers from getting garbage collected by V8,
we used to store a v8::Persistent<ArrayBuffer> in a map. This patch
introduces a custom ArrayBuffer allocator which doesn't use Persistent
handles, but instead stores a pointer to the actual ArrayBuffer data
alongside with a reference count. Since creating Persistent handles
has quite a bit of overhead, this change significantly increases
performance. Various HTTP server benchmarks report about 5-10% more
requests per second than before.
* Previously the Persistent handle that prevented garbage collection had
to be released manually, and this wasn't always done, which was
causing memory leaks. This has been resolved by introducing a new
`PinnedBuf` type in both Rust and C++ that automatically re-enables
garbage collection when it goes out of scope.
* Zero-copy buffers are now correctly wrapped in an Option if there is a
possibility that they're not present. This clears up a correctness
issue where we were creating zero-length slices from a null pointer,
which is against the rules.
Diffstat (limited to 'core/libdeno/binding.cc')
-rw-r--r-- | core/libdeno/binding.cc | 27 |
1 files changed, 6 insertions, 21 deletions
diff --git a/core/libdeno/binding.cc b/core/libdeno/binding.cc index ab633f46d..80f733b4f 100644 --- a/core/libdeno/binding.cc +++ b/core/libdeno/binding.cc @@ -163,9 +163,6 @@ 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>(); } @@ -248,11 +245,9 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) { DenoIsolate* d = DenoIsolate::FromIsolate(isolate); DCHECK_EQ(d->isolate_, isolate); - deno_buf control = {nullptr, 0u, nullptr, 0u, 0u}; - deno_buf zero_copy = {nullptr, 0u, nullptr, 0u, 0u}; - v8::HandleScope handle_scope(isolate); + deno_buf control = {nullptr, 0u, nullptr, 0u}; if (args.Length() > 0) { v8::Local<v8::Value> control_v = args[0]; if (control_v->IsArrayBufferView()) { @@ -261,25 +256,15 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) { } } - v8::Local<v8::Value> zero_copy_v; - if (args.Length() == 2) { - if (args[1]->IsArrayBufferView()) { - 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); - } - } + PinnedBuf zero_copy = + args[1]->IsArrayBufferView() + ? PinnedBuf(v8::Local<v8::ArrayBufferView>::Cast(args[1])) + : PinnedBuf(); DCHECK_NULL(d->current_args_); d->current_args_ = &args; - d->recv_cb_(d->user_data_, control, zero_copy); + d->recv_cb_(d->user_data_, control, zero_copy.IntoRaw()); if (d->current_args_ == nullptr) { // This indicates that deno_repond() was called already. |