Skip to content

Commit 96e800c

Browse files
authored
Merge branch 'nodejs:main' into diangogav/test-tls-check-server-identity-coverage
2 parents 434b1d3 + d2ff9da commit 96e800c

File tree

18 files changed

+482
-27
lines changed

18 files changed

+482
-27
lines changed

doc/api/process.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3013,7 +3013,7 @@ is drained immediately after.
30133013
30143014
So in CJS modules `process.nextTick()` callbacks are always run before `queueMicrotask()` ones.
30153015
However since ESM modules are processed already as part of the microtask queue, there
3016-
`queueMicrotask()` callbacks are always exectued before `process.nextTick()` ones since Node.js
3016+
`queueMicrotask()` callbacks are always executed before `process.nextTick()` ones since Node.js
30173017
is already in the process of draining the microtask queue.
30183018
30193019
```mjs

doc/api/v8.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,33 @@ added: v24.8.0
14251425

14261426
Stopping collecting the profile and the profile will be discarded.
14271427

1428+
## Class: `HeapProfileHandle`
1429+
1430+
<!-- YAML
1431+
added: REPLACEME
1432+
-->
1433+
1434+
### `heapProfileHandle.stop()`
1435+
1436+
<!-- YAML
1437+
added: REPLACEME
1438+
-->
1439+
1440+
* Returns: {Promise}
1441+
1442+
Stopping collecting the profile, then return a Promise that fulfills with an error or the
1443+
profile data.
1444+
1445+
### `heapProfileHandle[Symbol.asyncDispose]()`
1446+
1447+
<!-- YAML
1448+
added: REPLACEME
1449+
-->
1450+
1451+
* Returns: {Promise}
1452+
1453+
Stopping collecting the profile and the profile will be discarded.
1454+
14281455
## `v8.isStringOneByteRepresentation(content)`
14291456

14301457
<!-- YAML

doc/api/worker_threads.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,49 @@ w.on('online', async () => {
20012001
});
20022002
```
20032003
2004+
### `worker.startHeapProfile()`
2005+
2006+
<!-- YAML
2007+
added: REPLACEME
2008+
-->
2009+
2010+
* Returns: {Promise}
2011+
2012+
Starting a Heap profile then return a Promise that fulfills with an error
2013+
or an `HeapProfileHandle` object. This API supports `await using` syntax.
2014+
2015+
```cjs
2016+
const { Worker } = require('node:worker_threads');
2017+
2018+
const worker = new Worker(`
2019+
const { parentPort } = require('worker_threads');
2020+
parentPort.on('message', () => {});
2021+
`, { eval: true });
2022+
2023+
worker.on('online', async () => {
2024+
const handle = await worker.startHeapProfile();
2025+
const profile = await handle.stop();
2026+
console.log(profile);
2027+
worker.terminate();
2028+
});
2029+
```
2030+
2031+
`await using` example.
2032+
2033+
```cjs
2034+
const { Worker } = require('node::worker_threads');
2035+
2036+
const w = new Worker(`
2037+
const { parentPort } = require('worker_threads');
2038+
parentPort.on('message', () => {});
2039+
`, { eval: true });
2040+
2041+
w.on('online', async () => {
2042+
// Stop profile automatically when return and profile will be discarded
2043+
await using handle = await w.startHeapProfile();
2044+
});
2045+
```
2046+
20042047
### `worker.stderr`
20052048
20062049
<!-- YAML

lib/internal/worker.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,35 @@ class CPUProfileHandle {
165165
}
166166
}
167167

168+
class HeapProfileHandle {
169+
#worker = null;
170+
#promise = null;
171+
172+
constructor(worker) {
173+
this.#worker = worker;
174+
}
175+
176+
stop() {
177+
if (this.#promise) {
178+
return this.#promise;
179+
}
180+
const stopTaker = this.#worker[kHandle]?.stopHeapProfile();
181+
return this.#promise = new Promise((resolve, reject) => {
182+
if (!stopTaker) return reject(new ERR_WORKER_NOT_RUNNING());
183+
stopTaker.ondone = (err, profile) => {
184+
if (err) {
185+
return reject(err);
186+
}
187+
resolve(profile);
188+
};
189+
});
190+
};
191+
192+
async [SymbolAsyncDispose]() {
193+
await this.stop();
194+
}
195+
}
196+
168197
class Worker extends EventEmitter {
169198
constructor(filename, options = kEmptyObject) {
170199
throwIfBuildingSnapshot('Creating workers');
@@ -551,6 +580,19 @@ class Worker extends EventEmitter {
551580
};
552581
});
553582
}
583+
584+
startHeapProfile() {
585+
const startTaker = this[kHandle]?.startHeapProfile();
586+
return new Promise((resolve, reject) => {
587+
if (!startTaker) return reject(new ERR_WORKER_NOT_RUNNING());
588+
startTaker.ondone = (err) => {
589+
if (err) {
590+
return reject(err);
591+
}
592+
resolve(new HeapProfileHandle(this));
593+
};
594+
});
595+
}
554596
}
555597

556598
/**

src/async_wrap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ namespace node {
8181
V(WORKER) \
8282
V(WORKERCPUPROFILE) \
8383
V(WORKERCPUUSAGE) \
84+
V(WORKERHEAPPROFILE) \
8485
V(WORKERHEAPSNAPSHOT) \
8586
V(WORKERHEAPSTATISTICS) \
8687
V(WRITEWRAP) \

src/env_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@
458458
V(write_wrap_template, v8::ObjectTemplate) \
459459
V(worker_cpu_profile_taker_template, v8::ObjectTemplate) \
460460
V(worker_cpu_usage_taker_template, v8::ObjectTemplate) \
461+
V(worker_heap_profile_taker_template, v8::ObjectTemplate) \
461462
V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \
462463
V(worker_heap_statistics_taker_template, v8::ObjectTemplate) \
463464
V(x509_constructor_template, v8::FunctionTemplate) \

src/node_errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
8787
V(ERR_FS_CP_SOCKET, Error) \
8888
V(ERR_FS_CP_FIFO_PIPE, Error) \
8989
V(ERR_FS_CP_UNKNOWN, Error) \
90+
V(ERR_HEAP_PROFILE_HAVE_BEEN_STARTED, Error) \
91+
V(ERR_HEAP_PROFILE_NOT_STARTED, Error) \
9092
V(ERR_ILLEGAL_CONSTRUCTOR, Error) \
9193
V(ERR_INVALID_ADDRESS, Error) \
9294
V(ERR_INVALID_ARG_VALUE, TypeError) \

src/node_sqlite.cc

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,26 +1668,28 @@ void Backup(const FunctionCallbackInfo<Value>& args) {
16681668
job->ScheduleBackup();
16691669
}
16701670

1671+
struct ConflictCallbackContext {
1672+
std::function<bool(std::string_view)> filterCallback;
1673+
std::function<int(int)> conflictCallback;
1674+
};
1675+
16711676
// the reason for using static functions here is that SQLite needs a
16721677
// function pointer
1673-
static std::function<int(int)> conflictCallback;
16741678

16751679
static int xConflict(void* pCtx, int eConflict, sqlite3_changeset_iter* pIter) {
1676-
if (!conflictCallback) return SQLITE_CHANGESET_ABORT;
1677-
return conflictCallback(eConflict);
1680+
auto ctx = static_cast<ConflictCallbackContext*>(pCtx);
1681+
if (!ctx->conflictCallback) return SQLITE_CHANGESET_ABORT;
1682+
return ctx->conflictCallback(eConflict);
16781683
}
16791684

1680-
static std::function<bool(std::string)> filterCallback;
1681-
16821685
static int xFilter(void* pCtx, const char* zTab) {
1683-
if (!filterCallback) return 1;
1684-
1685-
return filterCallback(zTab) ? 1 : 0;
1686+
auto ctx = static_cast<ConflictCallbackContext*>(pCtx);
1687+
if (!ctx->filterCallback) return 1;
1688+
return ctx->filterCallback(zTab) ? 1 : 0;
16861689
}
16871690

16881691
void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1689-
conflictCallback = nullptr;
1690-
filterCallback = nullptr;
1692+
ConflictCallbackContext context;
16911693

16921694
DatabaseSync* db;
16931695
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -1723,7 +1725,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
17231725
return;
17241726
}
17251727
Local<Function> conflictFunc = conflictValue.As<Function>();
1726-
conflictCallback = [env, conflictFunc](int conflictType) -> int {
1728+
context.conflictCallback = [env, conflictFunc](int conflictType) -> int {
17271729
Local<Value> argv[] = {Integer::New(env->isolate(), conflictType)};
17281730
TryCatch try_catch(env->isolate());
17291731
Local<Value> result =
@@ -1761,15 +1763,18 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
17611763

17621764
Local<Function> filterFunc = filterValue.As<Function>();
17631765

1764-
filterCallback = [env, filterFunc](std::string item) -> bool {
1766+
context.filterCallback = [env,
1767+
filterFunc](std::string_view item) -> bool {
17651768
// TODO(@jasnell): The use of ToLocalChecked here means that if
17661769
// the filter function throws an error the process will crash.
17671770
// The filterCallback should be updated to avoid the check and
17681771
// propagate the error correctly.
1769-
Local<Value> argv[] = {String::NewFromUtf8(env->isolate(),
1770-
item.c_str(),
1771-
NewStringType::kNormal)
1772-
.ToLocalChecked()};
1772+
Local<Value> argv[] = {
1773+
String::NewFromUtf8(env->isolate(),
1774+
item.data(),
1775+
NewStringType::kNormal,
1776+
static_cast<int>(item.size()))
1777+
.ToLocalChecked()};
17731778
Local<Value> result =
17741779
filterFunc->Call(env->context(), Null(env->isolate()), 1, argv)
17751780
.ToLocalChecked();
@@ -1785,7 +1790,7 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
17851790
const_cast<void*>(static_cast<const void*>(buf.data())),
17861791
xFilter,
17871792
xConflict,
1788-
nullptr);
1793+
static_cast<void*>(&context));
17891794
if (r == SQLITE_OK) {
17901795
args.GetReturnValue().Set(true);
17911796
return;

0 commit comments

Comments
 (0)