Skip to content

Commit c971497

Browse files
committed
src: turn AllocatedBuffer into thin wrapper around v8::BackingStore
Alternative to #33381 that reimplements that change on top of moving AllocatedBuffer out of env.h
1 parent d575385 commit c971497

File tree

9 files changed

+116
-194
lines changed

9 files changed

+116
-194
lines changed

src/allocated_buffer-inl.h

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -11,97 +11,97 @@
1111

1212
namespace node {
1313

14+
// It's a bit awkward to define this Buffer::New() overload here, but it
15+
// avoids a circular dependency with node_internals.h.
16+
namespace Buffer {
17+
v8::MaybeLocal<v8::Uint8Array> New(Environment* env,
18+
v8::Local<v8::ArrayBuffer> ab,
19+
size_t byte_offset,
20+
size_t length);
21+
}
22+
23+
NoArrayBufferZeroFillScope::NoArrayBufferZeroFillScope(
24+
IsolateData* isolate_data)
25+
: node_allocator_(isolate_data->node_allocator()) {
26+
if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 0;
27+
}
28+
29+
NoArrayBufferZeroFillScope::~NoArrayBufferZeroFillScope() {
30+
if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 1;
31+
}
32+
1433
AllocatedBuffer AllocatedBuffer::AllocateManaged(
1534
Environment* env,
16-
size_t size,
17-
int flags) {
18-
char* data = flags & ALLOCATE_MANAGED_UNCHECKED ?
19-
env->AllocateUnchecked(size) :
20-
env->Allocate(size);
21-
if (data == nullptr) size = 0;
22-
return AllocatedBuffer(env, uv_buf_init(data, size));
35+
size_t size) {
36+
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
37+
std::unique_ptr<v8::BackingStore> bs =
38+
v8::ArrayBuffer::NewBackingStore(env->isolate(), size);
39+
return AllocatedBuffer(env, std::move(bs));
2340
}
2441

25-
inline AllocatedBuffer::AllocatedBuffer(Environment* env, uv_buf_t buf)
26-
: env_(env), buffer_(buf) {}
42+
AllocatedBuffer::AllocatedBuffer(
43+
Environment* env, std::unique_ptr<v8::BackingStore> bs)
44+
: env_(env), backing_store_(std::move(bs)) {}
45+
46+
AllocatedBuffer::AllocatedBuffer(
47+
Environment* env, uv_buf_t buffer)
48+
: env_(env) {
49+
if (buffer.base == nullptr) return;
50+
auto map = env->released_allocated_buffers();
51+
auto it = map->find(buffer.base);
52+
CHECK_NE(it, map->end());
53+
backing_store_ = std::move(it->second);
54+
map->erase(it);
55+
}
2756

28-
inline void AllocatedBuffer::Resize(size_t len) {
29-
// The `len` check is to make sure we don't end up with `nullptr` as our base.
30-
char* new_data = env_->Reallocate(buffer_.base, buffer_.len,
31-
len > 0 ? len : 1);
32-
CHECK_NOT_NULL(new_data);
33-
buffer_ = uv_buf_init(new_data, len);
57+
void AllocatedBuffer::Resize(size_t len) {
58+
if (len == 0) {
59+
backing_store_ = v8::ArrayBuffer::NewBackingStore(env_->isolate(), 0);
60+
return;
61+
}
62+
NoArrayBufferZeroFillScope no_zero_fill_scope(env_->isolate_data());
63+
backing_store_ = v8::BackingStore::Reallocate(
64+
env_->isolate(), std::move(backing_store_), len);
3465
}
3566

3667
inline uv_buf_t AllocatedBuffer::release() {
37-
uv_buf_t ret = buffer_;
38-
buffer_ = uv_buf_init(nullptr, 0);
68+
if (data() == nullptr) return uv_buf_init(nullptr, 0);
69+
70+
CHECK_NOT_NULL(env_);
71+
uv_buf_t ret = uv_buf_init(data(), size());
72+
env_->released_allocated_buffers()->emplace(
73+
ret.base, std::move(backing_store_));
3974
return ret;
4075
}
4176

4277
inline char* AllocatedBuffer::data() {
43-
return buffer_.base;
78+
if (!backing_store_) return nullptr;
79+
return static_cast<char*>(backing_store_->Data());
4480
}
4581

4682
inline const char* AllocatedBuffer::data() const {
47-
return buffer_.base;
83+
if (!backing_store_) return nullptr;
84+
return static_cast<char*>(backing_store_->Data());
4885
}
4986

50-
inline size_t AllocatedBuffer::size() const {
51-
return buffer_.len;
52-
}
53-
54-
inline AllocatedBuffer::AllocatedBuffer(Environment* env)
55-
: env_(env), buffer_(uv_buf_init(nullptr, 0)) {}
56-
57-
inline AllocatedBuffer::AllocatedBuffer(AllocatedBuffer&& other)
58-
: AllocatedBuffer() {
59-
*this = std::move(other);
60-
}
61-
62-
inline AllocatedBuffer& AllocatedBuffer::operator=(AllocatedBuffer&& other) {
63-
clear();
64-
env_ = other.env_;
65-
buffer_ = other.release();
66-
return *this;
67-
}
6887

69-
inline AllocatedBuffer::~AllocatedBuffer() {
70-
clear();
88+
inline size_t AllocatedBuffer::size() const {
89+
if (!backing_store_) return 0;
90+
return backing_store_->ByteLength();
7191
}
7292

7393
inline void AllocatedBuffer::clear() {
74-
uv_buf_t buf = release();
75-
if (buf.base != nullptr) {
76-
CHECK_NOT_NULL(env_);
77-
env_->Free(buf.base, buf.len);
78-
}
94+
backing_store_.reset();
7995
}
8096

8197
inline v8::MaybeLocal<v8::Object> AllocatedBuffer::ToBuffer() {
82-
CHECK_NOT_NULL(env_);
83-
v8::MaybeLocal<v8::Object> obj = Buffer::New(env_, data(), size(), false);
84-
if (!obj.IsEmpty()) release();
85-
return obj;
98+
v8::Local<v8::ArrayBuffer> ab = ToArrayBuffer();
99+
return Buffer::New(env_, ab, 0, ab->ByteLength())
100+
.FromMaybe(v8::Local<v8::Uint8Array>());
86101
}
87102

88103
inline v8::Local<v8::ArrayBuffer> AllocatedBuffer::ToArrayBuffer() {
89-
CHECK_NOT_NULL(env_);
90-
uv_buf_t buf = release();
91-
auto callback = [](void* data, size_t length, void* deleter_data){
92-
CHECK_NOT_NULL(deleter_data);
93-
94-
static_cast<v8::ArrayBuffer::Allocator*>(deleter_data)
95-
->Free(data, length);
96-
};
97-
std::unique_ptr<v8::BackingStore> backing =
98-
v8::ArrayBuffer::NewBackingStore(buf.base,
99-
buf.len,
100-
callback,
101-
env_->isolate()
102-
->GetArrayBufferAllocator());
103-
return v8::ArrayBuffer::New(env_->isolate(),
104-
std::move(backing));
104+
return v8::ArrayBuffer::New(env_->isolate(), std::move(backing_store_));
105105
}
106106

107107
} // namespace node

src/allocated_buffer.h

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,43 @@
66
#include "base_object.h"
77
#include "uv.h"
88
#include "v8.h"
9+
#include "env.h"
910

1011
namespace node {
1112

1213
class Environment;
1314

15+
// Disables zero-filling for ArrayBuffer allocations in this scope. This is
16+
// similar to how we implement Buffer.allocUnsafe() in JS land.
17+
class NoArrayBufferZeroFillScope{
18+
public:
19+
inline explicit NoArrayBufferZeroFillScope(IsolateData* isolate_data);
20+
inline ~NoArrayBufferZeroFillScope();
21+
22+
private:
23+
NodeArrayBufferAllocator* node_allocator_;
24+
25+
friend class Environment;
26+
};
27+
1428
// A unique-pointer-ish object that is compatible with the JS engine's
1529
// ArrayBuffer::Allocator.
30+
// TODO(addaleax): We may want to start phasing this out as it's only a
31+
// thin wrapper around v8::BackingStore at this point
1632
struct AllocatedBuffer {
1733
public:
18-
enum AllocateManagedFlags {
19-
ALLOCATE_MANAGED_FLAG_NONE,
20-
ALLOCATE_MANAGED_UNCHECKED
21-
};
22-
2334
// Utilities that allocate memory using the Isolate's ArrayBuffer::Allocator.
2435
// In particular, using AllocateManaged() will provide a RAII-style object
2536
// with easy conversion to `Buffer` and `ArrayBuffer` objects.
26-
inline static AllocatedBuffer AllocateManaged(
27-
Environment* env,
28-
size_t size,
29-
int flags = ALLOCATE_MANAGED_FLAG_NONE);
30-
31-
explicit inline AllocatedBuffer(Environment* env = nullptr);
32-
inline AllocatedBuffer(Environment* env, uv_buf_t buf);
33-
inline ~AllocatedBuffer();
37+
inline static AllocatedBuffer AllocateManaged(Environment* env, size_t size);
38+
39+
AllocatedBuffer() = default;
40+
inline AllocatedBuffer(
41+
Environment* env, std::unique_ptr<v8::BackingStore> bs);
42+
// For this constructor variant, `buffer` *must* come from an earlier call
43+
// to .release
44+
inline AllocatedBuffer(Environment* env, uv_buf_t buffer);
45+
3446
inline void Resize(size_t len);
3547

3648
inline uv_buf_t release();
@@ -42,16 +54,14 @@ struct AllocatedBuffer {
4254
inline v8::MaybeLocal<v8::Object> ToBuffer();
4355
inline v8::Local<v8::ArrayBuffer> ToArrayBuffer();
4456

45-
inline AllocatedBuffer(AllocatedBuffer&& other);
46-
inline AllocatedBuffer& operator=(AllocatedBuffer&& other);
57+
AllocatedBuffer(AllocatedBuffer&& other) = default;
58+
AllocatedBuffer& operator=(AllocatedBuffer&& other) = default;
4759
AllocatedBuffer(const AllocatedBuffer& other) = delete;
4860
AllocatedBuffer& operator=(const AllocatedBuffer& other) = delete;
4961

5062
private:
51-
Environment* env_;
52-
// We do not pass this to libuv directly, but uv_buf_t is a convenient way
53-
// to represent a chunk of memory, and plays nicely with other parts of core.
54-
uv_buf_t buffer_;
63+
Environment* env_ = nullptr;
64+
std::unique_ptr<v8::BackingStore> backing_store_;
5565

5666
friend class Environment;
5767
};

src/env-inl.h

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -867,29 +867,9 @@ inline IsolateData* Environment::isolate_data() const {
867867
return isolate_data_;
868868
}
869869

870-
inline char* Environment::AllocateUnchecked(size_t size) {
871-
return static_cast<char*>(
872-
isolate_data()->allocator()->AllocateUninitialized(size));
873-
}
874-
875-
inline char* Environment::Allocate(size_t size) {
876-
char* ret = AllocateUnchecked(size);
877-
CHECK_NE(ret, nullptr);
878-
return ret;
879-
}
880-
881-
inline void Environment::Free(char* data, size_t size) {
882-
if (data != nullptr)
883-
isolate_data()->allocator()->Free(data, size);
884-
}
885-
886-
// It's a bit awkward to define this Buffer::New() overload here, but it
887-
// avoids a circular dependency with node_internals.h.
888-
namespace Buffer {
889-
v8::MaybeLocal<v8::Object> New(Environment* env,
890-
char* data,
891-
size_t length,
892-
bool uses_malloc);
870+
std::unordered_map<char*, std::unique_ptr<v8::BackingStore>>*
871+
Environment::released_allocated_buffers() {
872+
return &released_allocated_buffers_;
893873
}
894874

895875
inline void Environment::ThrowError(const char* errmsg) {

src/env.cc

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,25 +1133,6 @@ void Environment::MemoryInfo(MemoryTracker* tracker) const {
11331133
// node, we shift its sizeof() size out of the Environment node.
11341134
}
11351135

1136-
char* Environment::Reallocate(char* data, size_t old_size, size_t size) {
1137-
if (old_size == size) return data;
1138-
// If we know that the allocator is our ArrayBufferAllocator, we can let
1139-
// if reallocate directly.
1140-
if (isolate_data()->uses_node_allocator()) {
1141-
return static_cast<char*>(
1142-
isolate_data()->node_allocator()->Reallocate(data, old_size, size));
1143-
}
1144-
// Generic allocators do not provide a reallocation method; we need to
1145-
// allocate a new chunk of memory and copy the data over.
1146-
char* new_data = AllocateUnchecked(size);
1147-
if (new_data == nullptr) return nullptr;
1148-
memcpy(new_data, data, std::min(size, old_size));
1149-
if (size > old_size)
1150-
memset(new_data + old_size, 0, size - old_size);
1151-
Free(data, old_size);
1152-
return new_data;
1153-
}
1154-
11551136
void Environment::RunWeakRefCleanup() {
11561137
isolate()->ClearKeptObjects();
11571138
}

src/env.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -925,11 +925,6 @@ class Environment : public MemoryRetainer {
925925

926926
inline IsolateData* isolate_data() const;
927927

928-
inline char* Allocate(size_t size);
929-
inline char* AllocateUnchecked(size_t size);
930-
char* Reallocate(char* data, size_t old_size, size_t size);
931-
inline void Free(char* data, size_t size);
932-
933928
inline bool printed_error() const;
934929
inline void set_printed_error(bool value);
935930

@@ -1216,6 +1211,9 @@ class Environment : public MemoryRetainer {
12161211
void RunAndClearNativeImmediates(bool only_refed = false);
12171212
void RunAndClearInterrupts();
12181213

1214+
inline std::unordered_map<char*, std::unique_ptr<v8::BackingStore>>*
1215+
released_allocated_buffers();
1216+
12191217
private:
12201218
template <typename Fn>
12211219
inline void CreateImmediate(Fn&& cb, bool ref);
@@ -1378,6 +1376,11 @@ class Environment : public MemoryRetainer {
13781376
// We should probably find a way to just use plain `v8::String`s created from
13791377
// the source passed to LoadEnvironment() directly instead.
13801378
std::unique_ptr<v8::String::Value> main_utf16_;
1379+
1380+
// Used by AllocatedBuffer::release() to keep track of the BackingStore for
1381+
// a given pointer.
1382+
std::unordered_map<char*, std::unique_ptr<v8::BackingStore>>
1383+
released_allocated_buffers_;
13811384
};
13821385

13831386
} // namespace node

0 commit comments

Comments
 (0)