Skip to content

Commit d91b489

Browse files
legendecasbengl
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 <joyeec9h3@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 11c783f commit d91b489

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
@@ -63,18 +63,18 @@ Worker::Worker(Environment* env,
6363
thread_id_.id);
6464

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

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

75-
object()->Set(env->context(),
76-
env->message_port_string(),
77-
parent_port_->object()).Check();
75+
object()
76+
->Set(env->context(), env->message_port_string(), parent_port->object())
77+
.Check();
7878

7979
object()->Set(env->context(),
8080
env->thread_id_string(),
@@ -734,10 +734,6 @@ void Worker::Exit(int code, const char* error_code, const char* error_message) {
734734
}
735735
}
736736

737-
void Worker::MemoryInfo(MemoryTracker* tracker) const {
738-
tracker->TrackField("parent_port", parent_port_);
739-
}
740-
741737
bool Worker::IsNotIndicativeOfMemoryLeakAtExit() const {
742738
// Worker objects always stay alive as long as the child thread, regardless
743739
// of whether they are being referenced in the parent thread.

src/node_worker.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Worker : public AsyncWrap {
5151
template <typename Fn>
5252
inline bool RequestInterrupt(Fn&& cb);
5353

54-
void MemoryInfo(MemoryTracker* tracker) const override;
54+
SET_NO_MEMORY_INFO()
5555
SET_MEMORY_INFO_NAME(Worker)
5656
SET_SELF_SIZE(Worker)
5757
bool IsNotIndicativeOfMemoryLeakAtExit() const override;
@@ -111,10 +111,6 @@ class Worker : public AsyncWrap {
111111
std::unique_ptr<MessagePortData> child_port_data_;
112112
std::shared_ptr<KVStore> env_vars_;
113113

114-
// This is always kept alive because the JS object associated with the Worker
115-
// instance refers to it via its [kPort] property.
116-
MessagePort* parent_port_ = nullptr;
117-
118114
// A raw flag that is used by creator and worker threads to
119115
// sync up on pre-mature termination of worker - while in the
120116
// 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)