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 d70aab6

Browse filesBrowse files
committed
src: support WeakReference in snapshot
Move util::WeakReference to a separate header and implement {de}serialization for it to be snapshotable. PR-URL: #44193 Refs: #44014 Refs: #37476 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent dab2057 commit d70aab6
Copy full SHA for d70aab6

File tree

Expand file treeCollapse file tree

9 files changed

+246
-38
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

9 files changed

+246
-38
lines changed
Open diff view settings
Collapse file

‎node.gyp‎

Copy file name to clipboardExpand all lines: node.gyp
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@
628628
'src/node_stat_watcher.h',
629629
'src/node_union_bytes.h',
630630
'src/node_url.h',
631+
'src/node_util.h',
631632
'src/node_version.h',
632633
'src/node_v8.h',
633634
'src/node_v8_platform-inl.h',
Collapse file

‎src/node_snapshotable.cc‎

Copy file name to clipboardExpand all lines: src/node_snapshotable.cc
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "node_metadata.h"
1616
#include "node_process.h"
1717
#include "node_snapshot_builder.h"
18+
#include "node_util.h"
1819
#include "node_v8.h"
1920
#include "node_v8_platform-inl.h"
2021

Collapse file

‎src/node_snapshotable.h‎

Copy file name to clipboardExpand all lines: src/node_snapshotable.h
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class ExternalReferenceRegistry;
1818
V(fs_binding_data, fs::BindingData) \
1919
V(v8_binding_data, v8_utils::BindingData) \
2020
V(blob_binding_data, BlobBindingData) \
21-
V(process_binding_data, process::BindingData)
21+
V(process_binding_data, process::BindingData) \
22+
V(util_weak_reference, util::WeakReference)
2223

2324
enum class EmbedderObjectType : uint8_t {
2425
#define V(PropertyName, NativeType) k_##PropertyName,
Collapse file

‎src/node_util.cc‎

Copy file name to clipboardExpand all lines: src/node_util.cc
+92-37Lines changed: 92 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "node_util.h"
12
#include "base_object-inl.h"
23
#include "node_errors.h"
34
#include "node_external_reference.h"
@@ -15,7 +16,7 @@ using v8::Context;
1516
using v8::External;
1617
using v8::FunctionCallbackInfo;
1718
using v8::FunctionTemplate;
18-
using v8::Global;
19+
using v8::HandleScope;
1920
using v8::IndexFilter;
2021
using v8::Integer;
2122
using v8::Isolate;
@@ -207,52 +208,106 @@ void ArrayBufferViewHasBuffer(const FunctionCallbackInfo<Value>& args) {
207208
args.GetReturnValue().Set(args[0].As<ArrayBufferView>()->HasBuffer());
208209
}
209210

210-
class WeakReference : public BaseObject {
211-
public:
212-
WeakReference(Environment* env, Local<Object> object, Local<Object> target)
213-
: BaseObject(env, object) {
214-
MakeWeak();
211+
WeakReference::WeakReference(Environment* env,
212+
Local<Object> object,
213+
Local<Object> target)
214+
: WeakReference(env, object, target, 0) {}
215+
216+
WeakReference::WeakReference(Environment* env,
217+
Local<Object> object,
218+
Local<Object> target,
219+
uint64_t reference_count)
220+
: SnapshotableObject(env, object, type_int),
221+
reference_count_(reference_count) {
222+
MakeWeak();
223+
if (!target.IsEmpty()) {
215224
target_.Reset(env->isolate(), target);
216-
target_.SetWeak();
225+
if (reference_count_ == 0) {
226+
target_.SetWeak();
227+
}
217228
}
229+
}
218230

219-
static void New(const FunctionCallbackInfo<Value>& args) {
220-
Environment* env = Environment::GetCurrent(args);
221-
CHECK(args.IsConstructCall());
222-
CHECK(args[0]->IsObject());
223-
new WeakReference(env, args.This(), args[0].As<Object>());
231+
bool WeakReference::PrepareForSerialization(Local<Context> context,
232+
v8::SnapshotCreator* creator) {
233+
if (target_.IsEmpty()) {
234+
target_index_ = 0;
235+
return true;
224236
}
225237

226-
static void Get(const FunctionCallbackInfo<Value>& args) {
227-
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
228-
Isolate* isolate = args.GetIsolate();
229-
if (!weak_ref->target_.IsEmpty())
230-
args.GetReturnValue().Set(weak_ref->target_.Get(isolate));
231-
}
238+
// Users can still hold strong references to target in addition to the
239+
// reference that we manage here, and they could expect that the referenced
240+
// object remains the same as long as that external strong reference
241+
// is alive. Since we have no way to know if there is any other reference
242+
// keeping the target alive, the best we can do to maintain consistency is to
243+
// simply save a reference to the target in the snapshot (effectively making
244+
// it strong) during serialization, and restore it during deserialization.
245+
// If there's no known counted reference from our side, we'll make the
246+
// reference here weak upon deserialization so that it can be GC'ed if users
247+
// do not hold additional references to it.
248+
Local<Object> target = target_.Get(context->GetIsolate());
249+
target_index_ = creator->AddData(context, target);
250+
DCHECK_NE(target_index_, 0);
251+
target_.Reset();
252+
return true;
253+
}
232254

233-
static void IncRef(const FunctionCallbackInfo<Value>& args) {
234-
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
235-
weak_ref->reference_count_++;
236-
if (weak_ref->target_.IsEmpty()) return;
237-
if (weak_ref->reference_count_ == 1) weak_ref->target_.ClearWeak();
238-
}
255+
InternalFieldInfoBase* WeakReference::Serialize(int index) {
256+
DCHECK_EQ(index, BaseObject::kEmbedderType);
257+
InternalFieldInfo* info =
258+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
259+
info->target = target_index_;
260+
info->reference_count = reference_count_;
261+
return info;
262+
}
239263

240-
static void DecRef(const FunctionCallbackInfo<Value>& args) {
241-
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
242-
CHECK_GE(weak_ref->reference_count_, 1);
243-
weak_ref->reference_count_--;
244-
if (weak_ref->target_.IsEmpty()) return;
245-
if (weak_ref->reference_count_ == 0) weak_ref->target_.SetWeak();
264+
void WeakReference::Deserialize(Local<Context> context,
265+
Local<Object> holder,
266+
int index,
267+
InternalFieldInfoBase* info) {
268+
DCHECK_EQ(index, BaseObject::kEmbedderType);
269+
HandleScope scope(context->GetIsolate());
270+
271+
InternalFieldInfo* weak_info = reinterpret_cast<InternalFieldInfo*>(info);
272+
Local<Object> target;
273+
if (weak_info->target != 0) {
274+
target = context->GetDataFromSnapshotOnce<Object>(weak_info->target)
275+
.ToLocalChecked();
246276
}
277+
new WeakReference(Environment::GetCurrent(context),
278+
holder,
279+
target,
280+
weak_info->reference_count);
281+
}
282+
283+
void WeakReference::New(const FunctionCallbackInfo<Value>& args) {
284+
Environment* env = Environment::GetCurrent(args);
285+
CHECK(args.IsConstructCall());
286+
CHECK(args[0]->IsObject());
287+
new WeakReference(env, args.This(), args[0].As<Object>());
288+
}
289+
290+
void WeakReference::Get(const FunctionCallbackInfo<Value>& args) {
291+
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
292+
Isolate* isolate = args.GetIsolate();
293+
if (!weak_ref->target_.IsEmpty())
294+
args.GetReturnValue().Set(weak_ref->target_.Get(isolate));
295+
}
247296

248-
SET_MEMORY_INFO_NAME(WeakReference)
249-
SET_SELF_SIZE(WeakReference)
250-
SET_NO_MEMORY_INFO()
297+
void WeakReference::IncRef(const FunctionCallbackInfo<Value>& args) {
298+
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
299+
weak_ref->reference_count_++;
300+
if (weak_ref->target_.IsEmpty()) return;
301+
if (weak_ref->reference_count_ == 1) weak_ref->target_.ClearWeak();
302+
}
251303

252-
private:
253-
Global<Object> target_;
254-
uint64_t reference_count_ = 0;
255-
};
304+
void WeakReference::DecRef(const FunctionCallbackInfo<Value>& args) {
305+
WeakReference* weak_ref = Unwrap<WeakReference>(args.Holder());
306+
CHECK_GE(weak_ref->reference_count_, 1);
307+
weak_ref->reference_count_--;
308+
if (weak_ref->target_.IsEmpty()) return;
309+
if (weak_ref->reference_count_ == 0) weak_ref->target_.SetWeak();
310+
}
256311

257312
static void GuessHandleType(const FunctionCallbackInfo<Value>& args) {
258313
Environment* env = Environment::GetCurrent(args);
Collapse file

‎src/node_util.h‎

Copy file name to clipboard
+54Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
#ifndef SRC_NODE_UTIL_H_
3+
#define SRC_NODE_UTIL_H_
4+
5+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
6+
#include "base_object.h"
7+
#include "node_snapshotable.h"
8+
#include "v8.h"
9+
10+
namespace node {
11+
namespace util {
12+
13+
class WeakReference : public SnapshotableObject {
14+
public:
15+
SERIALIZABLE_OBJECT_METHODS();
16+
17+
static constexpr FastStringKey type_name{"node::util::WeakReference"};
18+
static constexpr EmbedderObjectType type_int =
19+
EmbedderObjectType::k_util_weak_reference;
20+
21+
WeakReference(Environment* env,
22+
v8::Local<v8::Object> object,
23+
v8::Local<v8::Object> target);
24+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
25+
static void Get(const v8::FunctionCallbackInfo<v8::Value>& args);
26+
static void IncRef(const v8::FunctionCallbackInfo<v8::Value>& args);
27+
static void DecRef(const v8::FunctionCallbackInfo<v8::Value>& args);
28+
29+
SET_MEMORY_INFO_NAME(WeakReference)
30+
SET_SELF_SIZE(WeakReference)
31+
SET_NO_MEMORY_INFO()
32+
33+
struct InternalFieldInfo : public node::InternalFieldInfoBase {
34+
SnapshotIndex target;
35+
uint64_t reference_count;
36+
};
37+
38+
private:
39+
WeakReference(Environment* env,
40+
v8::Local<v8::Object> object,
41+
v8::Local<v8::Object> target,
42+
uint64_t reference_count);
43+
v8::Global<v8::Object> target_;
44+
uint64_t reference_count_ = 0;
45+
46+
SnapshotIndex target_index_ = 0; // 0 means target_ is not snapshotted
47+
};
48+
49+
} // namespace util
50+
} // namespace node
51+
52+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
53+
54+
#endif // SRC_NODE_UTIL_H_
Collapse file

‎src/util.cc‎

Copy file name to clipboardExpand all lines: src/util.cc
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "node_buffer.h"
2828
#include "node_errors.h"
2929
#include "node_internals.h"
30+
#include "node_util.h"
3031
#include "string_bytes.h"
3132
#include "uv.h"
3233

Collapse file
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
const { internalBinding } = require('internal/test/binding');
4+
const { WeakReference } = internalBinding('util');
5+
const {
6+
setDeserializeMainFunction
7+
} = require('v8').startupSnapshot
8+
const assert = require('assert');
9+
10+
let obj = { hello: 'world' };
11+
const ref = new WeakReference(obj);
12+
13+
setDeserializeMainFunction(() => {
14+
obj = null;
15+
globalThis.gc();
16+
17+
setImmediate(() => {
18+
assert.strictEqual(ref.get(), undefined);
19+
});
20+
});
Collapse file
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
const { internalBinding } = require('internal/test/binding');
4+
const { WeakReference } = internalBinding('util');
5+
const {
6+
setDeserializeMainFunction
7+
} = require('v8').startupSnapshot
8+
const assert = require('assert');
9+
10+
let obj = { hello: 'world' };
11+
const ref = new WeakReference(obj);
12+
13+
setDeserializeMainFunction(() => {
14+
assert.strictEqual(ref.get(), obj);
15+
});
Collapse file
+60Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
3+
// This tests that weak references work across serialization.
4+
5+
require('../common');
6+
const assert = require('assert');
7+
const { spawnSync } = require('child_process');
8+
const tmpdir = require('../common/tmpdir');
9+
const fixtures = require('../common/fixtures');
10+
const path = require('path');
11+
const fs = require('fs');
12+
13+
tmpdir.refresh();
14+
const blobPath = path.join(tmpdir.path, 'snapshot.blob');
15+
16+
function runTest(entry) {
17+
console.log('running test with', entry);
18+
{
19+
const child = spawnSync(process.execPath, [
20+
'--expose-internals',
21+
'--expose-gc',
22+
'--snapshot-blob',
23+
blobPath,
24+
'--build-snapshot',
25+
entry,
26+
], {
27+
cwd: tmpdir.path
28+
});
29+
if (child.status !== 0) {
30+
console.log(child.stderr.toString());
31+
console.log(child.stdout.toString());
32+
assert.strictEqual(child.status, 0);
33+
}
34+
const stats = fs.statSync(path.join(tmpdir.path, 'snapshot.blob'));
35+
assert(stats.isFile());
36+
}
37+
38+
{
39+
const child = spawnSync(process.execPath, [
40+
'--expose-internals',
41+
'--expose-gc',
42+
'--snapshot-blob',
43+
blobPath,
44+
], {
45+
cwd: tmpdir.path,
46+
env: {
47+
...process.env,
48+
}
49+
});
50+
51+
const stdout = child.stdout.toString().trim();
52+
const stderr = child.stderr.toString().trim();
53+
console.log(`[stdout]:\n${stdout}\n`);
54+
console.log(`[stderr]:\n${stderr}\n`);
55+
assert.strictEqual(child.status, 0);
56+
}
57+
}
58+
59+
runTest(fixtures.path('snapshot', 'weak-reference.js'));
60+
runTest(fixtures.path('snapshot', 'weak-reference-gc.js'));

0 commit comments

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