Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions benchmark/diagnostics_channel/threadpool-work.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';

const assert = require('node:assert');
const crypto = require('node:crypto');
const dc = require('node:diagnostics_channel');
const fs = require('node:fs');
const zlib = require('node:zlib');
const common = require('../common.js');
const tmpdir = require('../../test/common/tmpdir');

const bench = common.createBenchmark(main, {
n: [1e3, 1e4],
mode: ['subscribed', 'unsubscribed'],
operation: ['crypto', 'zlib', 'readFile', 'writeFile'],
});

function main({ n, mode, operation }) {
const subscriber = () => {};
const type = {
crypto: 'crypto',
zlib: 'zlib',
readFile: 'fs.readfile',
writeFile: 'fs.writefile',
}[operation];
assert(type);
const channel = `threadpool.work.${type}`;
if (mode === 'subscribed') {
dc.subscribe(channel, subscriber);
} else if (mode === 'unsubscribed') {
dc.subscribe(channel, subscriber);
dc.unsubscribe(channel, subscriber);
}

tmpdir.refresh();
const file = tmpdir.resolve('file');
const data = Buffer.alloc(1024, 'x');
fs.writeFileSync(file, data);

const operations = {
crypto(callback) {
crypto.pbkdf2('secret', 'salt', 1, 32, 'sha256', callback);
},
zlib(callback) {
zlib.gzip(data, callback);
},
readFile(callback) {
fs.readFile(file, callback);
},
writeFile(callback) {
fs.writeFile(file, data, callback);
},
};

let completed = 0;
bench.start();
run();

function run() {
operations[operation]((err) => {
assert.ifError(err);
if (++completed < n) return run();

bench.end(n);
if (mode === 'subscribed') {
dc.unsubscribe(channel, subscriber);
}
});
}
}
48 changes: 48 additions & 0 deletions doc/api/diagnostics_channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2026,6 +2026,53 @@ statement is garbage collected. Subscribers must not close the database or the
statement, since both are still in use while the event is being delivered; see
[`database.close()`][] and [`statement.close()`][].

#### Thread Pool

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

##### Event: `'threadpool.work.<type>'`

Each kind of thread pool work has a separate channel named by appending its
`type` to `'threadpool.work.'`.

Supported values for `<type>` are:

* `'crypto'`

* `'fs.cp'`

* `'fs.readfile'`

* `'fs.writefile'`

* `'glob'`

* `'histogram.qrde'`

* `'node_api'`

* `'readdir_recursive'`

* `'node_sqlite3.BackupJob'`

* `'zlib'`

Messages contain:

* `enqueued` {number} When the work was submitted to the pool.

* `started` {number|null} When execution started, or `null` if cancelled.

* `ended` {number|null} When execution ended, or `null` if cancelled.

Emitted after the work finishes and before its completion callback. Timestamps
use the [`performance.now()`][] timeline. `started - enqueued` is queue time;
`ended - started` is execution time.

[BoundedChannel Channels]: #boundedchannel-channels
[TracingChannel Channels]: #tracingchannel-channels
[`'uncaughtException'`]: process.md#event-uncaughtexception
Expand All @@ -2051,6 +2098,7 @@ statement, since both are still in use while the event is being delivered; see
[`error` event]: #errorevent
[`locks.request()`]: worker_threads.md#locksrequestname-options-callback
[`net.Server.listen()`]: net.md#serverlisten
[`performance.now()`]: perf_hooks.md#performancenow
[`process.execve()`]: process.md#processexecvefile-args-env
[`start` event]: #startevent
[`statement.close()`]: sqlite.md#statementclose
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/crypto_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ class CryptoJob : public AsyncWrap, public ThreadPoolWork {
CryptoJobMode mode,
AdditionalParams&& params)
: AsyncWrap(env, object, type),
ThreadPoolWork(env, "crypto"),
ThreadPoolWork(env, ThreadPoolWorkType::kCrypto),
mode_(mode),
params_(std::move(params)) {
// If the CryptoJob is async, then the instance will be
Expand Down
25 changes: 25 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "node_buffer.h"
#include "node_context_data.h"
#include "node_contextify.h"
#include "node_diagnostics_channel.h"
#include "node_errors.h"
#include "node_file_utils.h"
#include "node_internals.h"
Expand Down Expand Up @@ -1275,6 +1276,30 @@ Environment::~Environment() {
}
}

void Environment::InitializeThreadPoolWorkChannels() {
if (isolate_data()->is_building_snapshot()) return;

auto* binding =
principal_realm()->GetBindingData<diagnostics_channel::BindingData>();
CHECK_NOT_NULL(binding);
for (size_t i = 0; i < threadpool_work_channels_.size(); i++) {
ThreadPoolWorkChannel* entry = &threadpool_work_channels_[i];
if (entry->channel.get() != nullptr) continue;

std::string name = "threadpool.work.";
name += kThreadPoolWorkNames[i];
BaseObjectPtr<diagnostics_channel::Channel> channel =
diagnostics_channel::Channel::Get(this, name);
if (!channel) continue;

binding->SetChannelStatusCallback(
channel->index(), [entry](bool active) { entry->active = active; });
entry->channel =
BaseObjectWeakPtr<diagnostics_channel::Channel>(channel.get());
entry->active = channel->HasSubscribers();
}
}

void Environment::InitializeLibuv() {
HandleScope handle_scope(isolate());
Context::Scope context_scope(context());
Expand Down
45 changes: 45 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include <ostream>
#include <set>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <variant>
Expand All @@ -76,6 +77,42 @@ class MacCache;

namespace node {

namespace diagnostics_channel {
class Channel;
}

struct ThreadPoolWorkChannel {
BaseObjectWeakPtr<diagnostics_channel::Channel> channel;
bool active = false;
};

enum class ThreadPoolWorkType : uint8_t {
kCrypto,
kFsCp,
kFsReadFile,
kFsWriteFile,
kGlob,
kHistogramQrde,
kNodeApi,
kReadDirRecursive,
kSQLiteBackup,
kZlib,
kCount,
};

inline constexpr std::array<std::string_view,
static_cast<size_t>(ThreadPoolWorkType::kCount)>
kThreadPoolWorkNames = {"crypto",
"fs.cp",
"fs.readfile",
"fs.writefile",
"glob",
"histogram.qrde",
"node_api",
"readdir_recursive",
"node_sqlite3.BackupJob",
"zlib"};

namespace shadow_realm {
class ShadowRealm;
}
Expand Down Expand Up @@ -719,6 +756,11 @@ class Environment final : public MemoryRetainer {
void RunDeserializeRequests();
// Should be called before InitializeInspector()
void InitializeDiagnostics();
void InitializeThreadPoolWorkChannels();
inline ThreadPoolWorkChannel* threadpool_work_channel(
ThreadPoolWorkType type) {
return &threadpool_work_channels_[static_cast<size_t>(type)];
}

#if HAVE_INSPECTOR
// If the environment is created for a worker, pass parent_handle and
Expand Down Expand Up @@ -1221,6 +1263,9 @@ class Environment final : public MemoryRetainer {
AliasedInt32Array timeout_info_;
TickInfo tick_info_;
permission::Permission permission_;
std::array<ThreadPoolWorkChannel,
static_cast<size_t>(ThreadPoolWorkType::kCount)>
threadpool_work_channels_;
const uint64_t timer_base_;
std::shared_ptr<KVStore> env_vars_;
bool printed_error_ = false;
Expand Down
1 change: 1 addition & 0 deletions src/env_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@
V(srv_record_template, v8::DictionaryTemplate) \
V(streambaseoutputstream_constructor_template, v8::ObjectTemplate) \
V(tcp_constructor_template, v8::FunctionTemplate) \
V(threadpool_work_template, v8::DictionaryTemplate) \
V(tlsa_record_template, v8::DictionaryTemplate) \
V(tty_constructor_template, v8::FunctionTemplate) \
V(txt_record_template, v8::DictionaryTemplate) \
Expand Down
2 changes: 1 addition & 1 deletion src/glob/node_glob.cc
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ GlobRequest::GlobRequest(Environment* env,
const std::vector<CompiledPatternPtr>& excludes,
std::unique_ptr<JsExcludeFilter> filter)
: AsyncWrap(env, object, AsyncWrap::PROVIDER_GLOBREQUEST),
ThreadPoolWork(env, "glob"),
ThreadPoolWork(env, ThreadPoolWorkType::kGlob),
filter_(std::move(filter)),
walk_(env, options, includes, excludes),
with_file_types_(options.with_file_types) {
Expand Down
2 changes: 1 addition & 1 deletion src/histogram.cc
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ class QrdeJob final : public ThreadPoolWork {
Histogram::RecordedSnapshotSource snapshot_source,
std::vector<double> probabilities,
QrdeDequantization dequantization)
: ThreadPoolWork(env, "histogram.qrde"),
: ThreadPoolWork(env, ThreadPoolWorkType::kHistogramQrde),
histogram_(std::move(histogram)),
snapshot_source_(std::move(snapshot_source)),
probabilities_(std::move(probabilities)),
Expand Down
2 changes: 1 addition & 1 deletion src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,7 @@ class Work : public node::AsyncResource, public node::ThreadPoolWork {
env->isolate,
async_resource,
node::Utf8Value(env->isolate, async_resource_name).ToStringView()),
ThreadPoolWork(env->node_env(), "node_api"),
ThreadPoolWork(env->node_env(), node::ThreadPoolWorkType::kNodeApi),
_env(env),
_data(data),
_execute(execute),
Expand Down
3 changes: 3 additions & 0 deletions src/node_diagnostics_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "base_object-inl.h"
#include "env-inl.h"
#include "node_external_reference.h"
#include "node_internals.h"
#include "util-inl.h"
#include "v8.h"

Expand Down Expand Up @@ -94,6 +95,7 @@ void BindingData::LinkNativeChannel(const FunctionCallbackInfo<Value>& args) {
}
}
}
realm->env()->InitializeThreadPoolWorkChannels();
}

bool BindingData::PrepareForSerialization(Local<Context> context,
Expand All @@ -102,6 +104,7 @@ bool BindingData::PrepareForSerialization(Local<Context> context,
internal_field_info_ = InternalFieldInfoBase::New<InternalFieldInfo>(type());
internal_field_info_->subscribers = subscribers_.Serialize(context, creator);
internal_field_info_->subscribers_capacity = subscribers_.Length();
channel_status_callbacks_.clear();
link_callback_.Reset();
channel_wrap_template_.Reset();
channels_.clear();
Expand Down
2 changes: 2 additions & 0 deletions src/node_diagnostics_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class Channel : public BaseObject {

static BaseObjectPtr<Channel> Get(Environment* env, std::string_view name);

uint32_t index() const { return index_; }

inline bool HasSubscribers() const {
return binding_data_ != nullptr && binding_data_->subscribers_[index_] > 0;
}
Expand Down
8 changes: 4 additions & 4 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2724,7 +2724,7 @@ class ReadDirRecursiveWork final : public ThreadPoolWork {
public:
ReadDirRecursiveWork(Environment* env,
std::shared_ptr<ReadDirRecursiveRequest> request)
: ThreadPoolWork(env, "readdir_recursive"),
: ThreadPoolWork(env, ThreadPoolWorkType::kReadDirRecursive),
request_(std::move(request)) {}

void DoThreadPoolWork() override { request_->walk()->Run(); }
Expand Down Expand Up @@ -3769,7 +3769,7 @@ class ReadFileJob final : public AsyncWrap, public ThreadPoolWork {
uint64_t limit,
bool track_fd)
: AsyncWrap(env, object, AsyncWrap::PROVIDER_FSREQCALLBACK),
ThreadPoolWork(env, "fs.readfile"),
ThreadPoolWork(env, ThreadPoolWorkType::kFsReadFile),
path_(std::move(path)),
limit_(limit),
flags_(flags),
Expand Down Expand Up @@ -3957,7 +3957,7 @@ class WriteFileJob final : public AsyncWrap, public ThreadPoolWork {
int mode,
Local<ArrayBufferView> view)
: AsyncWrap(env, object, AsyncWrap::PROVIDER_FSREQCALLBACK),
ThreadPoolWork(env, "fs.writefile"),
ThreadPoolWork(env, ThreadPoolWorkType::kFsWriteFile),
path_(std::move(path)),
flags_(flags),
mode_(mode) {
Expand Down Expand Up @@ -5226,7 +5226,7 @@ class CpDirJob final : public AsyncWrap, public ThreadPoolWork {
std::string&& dest_display,
CpDirOptions options)
: AsyncWrap(env, object, AsyncWrap::PROVIDER_FSREQCALLBACK),
ThreadPoolWork(env, "fs.cp"),
ThreadPoolWork(env, ThreadPoolWorkType::kFsCp),
src_(std::move(src)),
dest_(std::move(dest)),
dest_display_(std::move(dest_display)),
Expand Down
Loading
Loading