summaryrefslogtreecommitdiff
path: root/core/libdeno/binding.cc
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2019-04-28 21:31:10 +0200
committerBert Belder <bertbelder@gmail.com>2019-05-01 21:11:09 +0200
commit41c7e96f1a81ea416ebb3ba45f2815e0202d6b75 (patch)
tree5fcb15b8664d7579cf4db76d16754c8efa7c4667 /core/libdeno/binding.cc
parentabdb98a2516a9d6ec313805dffbc2107d38f8ed4 (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.cc27
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.