Skip to content

Commit 835f0a5

Browse files
legendecasjuanarbol
authored andcommitted
worker: fix heap snapshot crash on exit
Worker.parent_port_ can be released before the exit event of Worker. The parent_port_ is not owned by `node::worker::Worker`, thus the reference to it is not always valid, and accessing it at exit crashes the process. As the Worker.parent_port_ is only used in the memory info tracking, it can be safely removed. PR-URL: #43123 Fixes: #43122 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 236d139 commit 835f0a5

File tree

4 files changed

+24
-23
lines changed

4 files changed

+24
-23
lines changed

src/node_worker.cc

+6-10
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,18 @@ Worker::Worker(Environment* env,
6161
thread_id_.id);
6262

6363
// Set up everything that needs to be set up in the parent environment.
64-
parent_port_ = MessagePort::New(env, env->context());
65-
if (parent_port_ == nullptr) {
64+
MessagePort* parent_port = MessagePort::New(env, env->context());
65+
if (parent_port == nullptr) {
6666
// This can happen e.g. because execution is terminating.
6767
return;
6868
}
6969

7070
child_port_data_ = std::make_unique<MessagePortData>(nullptr);
71-
MessagePort::Entangle(parent_port_, child_port_data_.get());
71+
MessagePort::Entangle(parent_port, child_port_data_.get());
7272

73-
object()->Set(env->context(),
74-
env->message_port_string(),
75-
parent_port_->object()).Check();
73+
object()
74+
->Set(env->context(), env->message_port_string(), parent_port->object())
75+
.Check();
7676

7777
object()->Set(env->context(),
7878
env->thread_id_string(),
@@ -724,10 +724,6 @@ void Worker::Exit(int code, const char* error_code, const char* error_message) {
724724
}
725725
}
726726

727-
void Worker::MemoryInfo(MemoryTracker* tracker) const {
728-
tracker->TrackField("parent_port", parent_port_);
729-
}
730-
731727
bool Worker::IsNotIndicativeOfMemoryLeakAtExit() const {
732728
// Worker objects always stay alive as long as the child thread, regardless
733729
// of whether they are being referenced in the parent thread.

src/node_worker.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Worker : public AsyncWrap {
4747
template <typename Fn>
4848
inline bool RequestInterrupt(Fn&& cb);
4949

50-
void MemoryInfo(MemoryTracker* tracker) const override;
50+
SET_NO_MEMORY_INFO()
5151
SET_MEMORY_INFO_NAME(Worker)
5252
SET_SELF_SIZE(Worker)
5353
bool IsNotIndicativeOfMemoryLeakAtExit() const override;
@@ -107,10 +107,6 @@ class Worker : public AsyncWrap {
107107
std::unique_ptr<MessagePortData> child_port_data_;
108108
std::shared_ptr<KVStore> env_vars_;
109109

110-
// This is always kept alive because the JS object associated with the Worker
111-
// instance refers to it via its [kPort] property.
112-
MessagePort* parent_port_ = nullptr;
113-
114110
// A raw flag that is used by creator and worker threads to
115111
// sync up on pre-mature termination of worker - while in the
116112
// warmup phase. Once the worker is fully warmed up, use the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const { getHeapSnapshot } = require('v8');
5+
const { isMainThread, Worker } = require('worker_threads');
6+
7+
// Checks taking heap snapshot at the exit event listener of Worker doesn't
8+
// crash the process.
9+
// Regression for https://github.com/nodejs/node/issues/43122.
10+
if (isMainThread) {
11+
const worker = new Worker(__filename);
12+
13+
worker.once('exit', common.mustCall((code) => {
14+
assert.strictEqual(code, 0);
15+
getHeapSnapshot().pipe(process.stdout);
16+
}));
17+
}

test/pummel/test-heapdump-worker.js

-8
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ const { Worker } = require('worker_threads');
66

77
validateSnapshotNodes('Node / Worker', []);
88
const worker = new Worker('setInterval(() => {}, 100);', { eval: true });
9-
validateSnapshotNodes('Node / Worker', [
10-
{
11-
children: [
12-
{ node_name: 'Node / MessagePort', edge_name: 'parent_port' },
13-
{ node_name: 'Worker', edge_name: 'wrapped' },
14-
]
15-
},
16-
]);
179
validateSnapshotNodes('Node / MessagePort', [
1810
{
1911
children: [

0 commit comments

Comments
 (0)