Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b2915cd

Browse filesBrowse files
addaleaxaduh95
authored andcommitted
src: extract zlib allocation tracking into its own class
This makes it a bit easier to separate concerns, and results in reduced code duplication when compiling since this code does not depend on template parameters. PR-URL: #61717 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent 3032a7e commit b2915cd
Copy full SHA for b2915cd

1 file changed

+80-54Lines changed: 80 additions & 54 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/node_zlib.cc‎

Copy file name to clipboardExpand all lines: src/node_zlib.cc
+80-54Lines changed: 80 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,68 @@ class ZstdDecompressContext final : public ZstdContext {
370370
DeleteFnPtr<ZSTD_DCtx, ZstdDecompressContext::FreeZstd> dctx_;
371371
};
372372

373+
class CompressionStreamMemoryOwner {
374+
public:
375+
// Allocation functions provided to zlib itself. We store the real size of
376+
// the allocated memory chunk just before the "payload" memory we return
377+
// to zlib.
378+
// Because we use zlib off the thread pool, we can not report memory directly
379+
// to V8; rather, we first store it as "unreported" memory in a separate
380+
// field and later report it back from the main thread.
381+
static void* AllocForZlib(void* data, uInt items, uInt size) {
382+
size_t real_size = MultiplyWithOverflowCheck(static_cast<size_t>(items),
383+
static_cast<size_t>(size));
384+
return AllocForBrotli(data, real_size);
385+
}
386+
387+
static constexpr size_t reserveSizeAndAlign =
388+
std::max(sizeof(size_t), alignof(max_align_t));
389+
390+
static void* AllocForBrotli(void* data, size_t size) {
391+
size += reserveSizeAndAlign;
392+
CompressionStreamMemoryOwner* ctx =
393+
static_cast<CompressionStreamMemoryOwner*>(data);
394+
char* memory = UncheckedMalloc(size);
395+
if (memory == nullptr) [[unlikely]] {
396+
return nullptr;
397+
}
398+
*reinterpret_cast<size_t*>(memory) = size;
399+
ctx->unreported_allocations_.fetch_add(size, std::memory_order_relaxed);
400+
return memory + reserveSizeAndAlign;
401+
}
402+
403+
static void FreeForZlib(void* data, void* pointer) {
404+
if (pointer == nullptr) [[unlikely]] {
405+
return;
406+
}
407+
CompressionStreamMemoryOwner* ctx =
408+
static_cast<CompressionStreamMemoryOwner*>(data);
409+
char* real_pointer = static_cast<char*>(pointer) - reserveSizeAndAlign;
410+
size_t real_size = *reinterpret_cast<size_t*>(real_pointer);
411+
ctx->unreported_allocations_.fetch_sub(real_size,
412+
std::memory_order_relaxed);
413+
free(real_pointer);
414+
}
415+
416+
void* as_allocator_opaque_value() { return static_cast<void*>(this); }
417+
418+
protected:
419+
ssize_t ComputeAdjustmentToExternalAllocatedMemory() {
420+
ssize_t report =
421+
unreported_allocations_.exchange(0, std::memory_order_relaxed);
422+
CHECK_IMPLIES(report < 0, zlib_memory_ >= static_cast<size_t>(-report));
423+
zlib_memory_ += report;
424+
return report;
425+
}
426+
427+
std::atomic<ssize_t> unreported_allocations_{0};
428+
size_t zlib_memory_ = 0;
429+
};
430+
373431
template <typename CompressionContext>
374-
class CompressionStream : public AsyncWrap, public ThreadPoolWork {
432+
class CompressionStream : public AsyncWrap,
433+
public ThreadPoolWork,
434+
protected CompressionStreamMemoryOwner {
375435
public:
376436
enum InternalFields {
377437
kCompressionStreamBaseField = AsyncWrap::kInternalFieldCount,
@@ -591,6 +651,19 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork {
591651
zlib_memory_ + unreported_allocations_);
592652
}
593653

654+
static void* AllocatorOpaquePointerForContext(CompressionContext* ctx) {
655+
CompressionStream* self = ContainerOf(&CompressionStream::ctx_, ctx);
656+
// There is nothing at the type level stopping someone from using this
657+
// method with `ctx` being an argument that is not part of a
658+
// CompressionStream. This check catches that potential discrepancy in debug
659+
// builds. std::launder is necessary to keep the compiler from optimizing
660+
// away the check in the (common) case that `CompressionContext` is a final
661+
// class.
662+
DCHECK_EQ(std::launder<MemoryRetainer>(&self->ctx_)->MemoryInfoName(),
663+
CompressionContext{}.MemoryInfoName());
664+
return self->as_allocator_opaque_value();
665+
}
666+
594667
protected:
595668
CompressionContext* context() { return &ctx_; }
596669

@@ -600,55 +673,11 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork {
600673
init_done_ = true;
601674
}
602675

603-
// Allocation functions provided to zlib itself. We store the real size of
604-
// the allocated memory chunk just before the "payload" memory we return
605-
// to zlib.
606-
// Because we use zlib off the thread pool, we can not report memory directly
607-
// to V8; rather, we first store it as "unreported" memory in a separate
608-
// field and later report it back from the main thread.
609-
static void* AllocForZlib(void* data, uInt items, uInt size) {
610-
size_t real_size =
611-
MultiplyWithOverflowCheck(static_cast<size_t>(items),
612-
static_cast<size_t>(size));
613-
return AllocForBrotli(data, real_size);
614-
}
615-
616-
static constexpr size_t reserveSizeAndAlign =
617-
std::max(sizeof(size_t), alignof(max_align_t));
618-
619-
static void* AllocForBrotli(void* data, size_t size) {
620-
size += reserveSizeAndAlign;
621-
CompressionStream* ctx = static_cast<CompressionStream*>(data);
622-
char* memory = UncheckedMalloc(size);
623-
if (memory == nullptr) [[unlikely]] {
624-
return nullptr;
625-
}
626-
*reinterpret_cast<size_t*>(memory) = size;
627-
ctx->unreported_allocations_.fetch_add(size,
628-
std::memory_order_relaxed);
629-
return memory + reserveSizeAndAlign;
630-
}
631-
632-
static void FreeForZlib(void* data, void* pointer) {
633-
if (pointer == nullptr) [[unlikely]] {
634-
return;
635-
}
636-
CompressionStream* ctx = static_cast<CompressionStream*>(data);
637-
char* real_pointer = static_cast<char*>(pointer) - reserveSizeAndAlign;
638-
size_t real_size = *reinterpret_cast<size_t*>(real_pointer);
639-
ctx->unreported_allocations_.fetch_sub(real_size,
640-
std::memory_order_relaxed);
641-
free(real_pointer);
642-
}
643-
644676
// This is called on the main thread after zlib may have allocated something
645677
// in order to report it back to V8.
646678
void AdjustAmountOfExternalAllocatedMemory() {
647-
ssize_t report =
648-
unreported_allocations_.exchange(0, std::memory_order_relaxed);
679+
ssize_t report = ComputeAdjustmentToExternalAllocatedMemory();
649680
if (report == 0) return;
650-
CHECK_IMPLIES(report < 0, zlib_memory_ >= static_cast<size_t>(-report));
651-
zlib_memory_ += report;
652681
AsyncWrap::env()->external_memory_accounter()->Update(
653682
AsyncWrap::env()->isolate(), report);
654683
}
@@ -679,8 +708,6 @@ class CompressionStream : public AsyncWrap, public ThreadPoolWork {
679708
bool closed_ = false;
680709
unsigned int refs_ = 0;
681710
uint32_t* write_result_ = nullptr;
682-
std::atomic<ssize_t> unreported_allocations_{0};
683-
size_t zlib_memory_ = 0;
684711

685712
CompressionContext ctx_;
686713
};
@@ -757,7 +784,7 @@ class ZlibStream final : public CompressionStream<ZlibContext> {
757784

758785
AllocScope alloc_scope(wrap);
759786
wrap->context()->SetAllocationFunctions(
760-
AllocForZlib, FreeForZlib, static_cast<CompressionStream*>(wrap));
787+
AllocForZlib, FreeForZlib, wrap->as_allocator_opaque_value());
761788
wrap->context()->Init(level, window_bits, mem_level, strategy,
762789
std::move(dictionary));
763790
}
@@ -819,11 +846,10 @@ class BrotliCompressionStream final :
819846
wrap->InitStream(write_result, write_js_callback);
820847

821848
AllocScope alloc_scope(wrap);
822-
CompressionError err =
823-
wrap->context()->Init(
824-
CompressionStream<CompressionContext>::AllocForBrotli,
825-
CompressionStream<CompressionContext>::FreeForZlib,
826-
static_cast<CompressionStream<CompressionContext>*>(wrap));
849+
CompressionError err = wrap->context()->Init(
850+
CompressionStream<CompressionContext>::AllocForBrotli,
851+
CompressionStream<CompressionContext>::FreeForZlib,
852+
wrap->as_allocator_opaque_value());
827853
if (err.IsError()) {
828854
wrap->EmitError(err);
829855
// TODO(addaleax): Sometimes we generate better error codes in C++ land,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.