Skip to content

Commit 584807b

Browse files
committed
Add runtime detection of V8 memory cage #3384
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.
1 parent a7fa701 commit 584807b

File tree

5 files changed

+19
-7
lines changed

5 files changed

+19
-7
lines changed

docs/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ Requires libvips v8.13.3
99
* Add experimental support for JPEG-XL images. Requires libvips compiled with libjxl.
1010
[#2731](https://github.com/lovell/sharp/issues/2731)
1111

12+
* Add runtime detection of V8 memory cage, ensures compatibility with Electron 21 onwards.
13+
[#3384](https://github.com/lovell/sharp/issues/3384)
14+
1215
* Expose `interFrameMaxError` and `interPaletteMaxError` GIF optimisation properties.
1316
[#3401](https://github.com/lovell/sharp/issues/3401)
1417

src/common.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ namespace sharp {
7676
}
7777
return vector;
7878
}
79+
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len) {
80+
try {
81+
return Napi::Buffer<char>::New(env, data, len, FreeCallback);
82+
} catch (Napi::Error const &err) {}
83+
Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, data, len);
84+
FreeCallback(nullptr, data);
85+
return buf;
86+
}
7987

8088
// Create an InputDescriptor instance from a Napi::Object describing an input image
8189
InputDescriptor* CreateInputDescriptor(Napi::Object input) {

src/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ namespace sharp {
133133
return static_cast<T>(
134134
vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
135135
}
136+
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len);
136137

137138
// Create an InputDescriptor instance from a Napi::Object describing an input image
138139
InputDescriptor* CreateInputDescriptor(Napi::Object input);

src/metadata.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,20 +235,20 @@ class MetadataWorker : public Napi::AsyncWorker {
235235
info.Set("orientation", baton->orientation);
236236
}
237237
if (baton->exifLength > 0) {
238-
info.Set("exif", Napi::Buffer<char>::New(env, baton->exif, baton->exifLength, sharp::FreeCallback));
238+
info.Set("exif", sharp::NewOrCopyBuffer(env, baton->exif, baton->exifLength));
239239
}
240240
if (baton->iccLength > 0) {
241-
info.Set("icc", Napi::Buffer<char>::New(env, baton->icc, baton->iccLength, sharp::FreeCallback));
241+
info.Set("icc", sharp::NewOrCopyBuffer(env, baton->icc, baton->iccLength));
242242
}
243243
if (baton->iptcLength > 0) {
244-
info.Set("iptc", Napi::Buffer<char>::New(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
244+
info.Set("iptc", sharp::NewOrCopyBuffer(env, baton->iptc, baton->iptcLength));
245245
}
246246
if (baton->xmpLength > 0) {
247-
info.Set("xmp", Napi::Buffer<char>::New(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
247+
info.Set("xmp", sharp::NewOrCopyBuffer(env, baton->xmp, baton->xmpLength));
248248
}
249249
if (baton->tifftagPhotoshopLength > 0) {
250250
info.Set("tifftagPhotoshop",
251-
Napi::Buffer<char>::New(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback));
251+
sharp::NewOrCopyBuffer(env, baton->tifftagPhotoshop, baton->tifftagPhotoshopLength));
252252
}
253253
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
254254
} else {

src/pipeline.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,8 +1206,8 @@ class PipelineWorker : public Napi::AsyncWorker {
12061206
// Add buffer size to info
12071207
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
12081208
// Pass ownership of output data to Buffer instance
1209-
Napi::Buffer<char> data = Napi::Buffer<char>::New(env, static_cast<char*>(baton->bufferOut),
1210-
baton->bufferOutLength, sharp::FreeCallback);
1209+
Napi::Buffer<char> data = sharp::NewOrCopyBuffer(env, static_cast<char*>(baton->bufferOut),
1210+
baton->bufferOutLength);
12111211
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
12121212
} else {
12131213
// Add file size to info

0 commit comments

Comments
 (0)