Skip to content

Commit

Permalink
Add runtime detection of V8 memory cage #3384
Browse files Browse the repository at this point in the history
When using the V8 memory cage, Buffers cannot be wrapped and then
later freed via a callback. When the cage is detected via a throw,
instead fall back to copying Buffer contents to V8 memory.

This approach will be used by Electron 21+ and you should expect
reduced performance and increased memory consumption/fragmentation.
  • Loading branch information
lovell committed Dec 14, 2022
1 parent a7fa701 commit 584807b
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 7 deletions.
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Requires libvips v8.13.3
* Add experimental support for JPEG-XL images. Requires libvips compiled with libjxl.
[#2731](https://github.com/lovell/sharp/issues/2731)

* Add runtime detection of V8 memory cage, ensures compatibility with Electron 21 onwards.
[#3384](https://github.com/lovell/sharp/issues/3384)

* Expose `interFrameMaxError` and `interPaletteMaxError` GIF optimisation properties.
[#3401](https://github.com/lovell/sharp/issues/3401)

Expand Down
8 changes: 8 additions & 0 deletions src/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ namespace sharp {
}
return vector;
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len) {
try {
return Napi::Buffer<char>::New(env, data, len, FreeCallback);
} catch (Napi::Error const &err) {}
Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, data, len);
FreeCallback(nullptr, data);
return buf;
}

// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input) {
Expand Down
1 change: 1 addition & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ namespace sharp {
return static_cast<T>(
vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
}
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len);

// Create an InputDescriptor instance from a Napi::Object describing an input image
InputDescriptor* CreateInputDescriptor(Napi::Object input);
Expand Down
10 changes: 5 additions & 5 deletions src/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,20 +235,20 @@ class MetadataWorker : public Napi::AsyncWorker {
info.Set("orientation", baton->orientation);
}
if (baton->exifLength > 0) {
info.Set("exif", Napi::Buffer<char>::New(env, baton->exif, baton->exifLength, sharp::FreeCallback));
info.Set("exif", sharp::NewOrCopyBuffer(env, baton->exif, baton->exifLength));
}
if (baton->iccLength > 0) {
info.Set("icc", Napi::Buffer<char>::New(env, baton->icc, baton->iccLength, sharp::FreeCallback));
info.Set("icc", sharp::NewOrCopyBuffer(env, baton->icc, baton->iccLength));
}
if (baton->iptcLength > 0) {
info.Set("iptc", Napi::Buffer<char>::New(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
info.Set("iptc", sharp::NewOrCopyBuffer(env, baton->iptc, baton->iptcLength));
}
if (baton->xmpLength > 0) {
info.Set("xmp", Napi::Buffer<char>::New(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
info.Set("xmp", sharp::NewOrCopyBuffer(env, baton->xmp, baton->xmpLength));
}
if (baton->tifftagPhotoshopLength > 0) {
info.Set("tifftagPhotoshop",
Napi::Buffer<char>::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback));
sharp::NewOrCopyBuffer(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength));
}
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1206,8 +1206,8 @@ class PipelineWorker : public Napi::AsyncWorker {
// Add buffer size to info
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
// Pass ownership of output data to Buffer instance
Napi::Buffer<char> data = Napi::Buffer<char>::New(env, static_cast<char*>(baton->bufferOut),
baton->bufferOutLength, sharp::FreeCallback);
Napi::Buffer<char> data = sharp::NewOrCopyBuffer(env, static_cast<char*>(baton->bufferOut),
baton->bufferOutLength);
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
} else {
// Add file size to info
Expand Down

0 comments on commit 584807b

Please sign in to comment.