From 05f0dfb38133214f6fc095469aa4045df5d6c82c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 1 Apr 2020 15:09:04 -0700 Subject: [PATCH 1/3] src: add CPUInfo utility class Utility helper that makes working with uv_cpu_info easier Signed-off-by: James M Snell --- src/node_os.cc | 26 +++++++++++--------------- src/node_report.cc | 9 ++++----- src/util.h | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/node_os.cc b/src/node_os.cc index b64b75fa6b90be..9a6717523d3df7 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -102,30 +102,26 @@ static void GetCPUInfo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); - uv_cpu_info_t* cpu_infos; - int count; + CPUInfo cpu_infos; - int err = uv_cpu_info(&cpu_infos, &count); - if (err) + if (!cpu_infos) return; // It's faster to create an array packed with all the data and // assemble them into objects in JS than to call Object::Set() repeatedly // The array is in the format // [model, speed, (5 entries of cpu_times), model2, speed2, ...] - std::vector> result(count * 7); - for (int i = 0, j = 0; i < count; i++) { - uv_cpu_info_t* ci = cpu_infos + i; - result[j++] = OneByteString(isolate, ci->model); - result[j++] = Number::New(isolate, ci->speed); - result[j++] = Number::New(isolate, ci->cpu_times.user); - result[j++] = Number::New(isolate, ci->cpu_times.nice); - result[j++] = Number::New(isolate, ci->cpu_times.sys); - result[j++] = Number::New(isolate, ci->cpu_times.idle); - result[j++] = Number::New(isolate, ci->cpu_times.irq); + std::vector> result(cpu_infos.count() * 7); + for (int i = 0, j = 0; i < cpu_infos.count(); i++) { + result[j++] = OneByteString(isolate, cpu_infos[i].model); + result[j++] = Number::New(isolate, cpu_infos[i].speed); + result[j++] = Number::New(isolate, cpu_infos[i].cpu_times.user); + result[j++] = Number::New(isolate, cpu_infos[i].cpu_times.nice); + result[j++] = Number::New(isolate, cpu_infos[i].cpu_times.sys); + result[j++] = Number::New(isolate, cpu_infos[i].cpu_times.idle); + result[j++] = Number::New(isolate, cpu_infos[i].cpu_times.irq); } - uv_free_cpu_info(cpu_infos, count); args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size())); } diff --git a/src/node_report.cc b/src/node_report.cc index 98da24c9567a28..20bafc65bece19 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -30,6 +30,7 @@ constexpr double SEC_PER_MICROS = 1e-6; namespace report { using node::arraysize; using node::ConditionVariable; +using node::CPUInfo; using node::DiagnosticFilename; using node::Environment; using node::JSONWriter; @@ -387,11 +388,10 @@ static void PrintVersionInformation(JSONWriter* writer) { // Report CPU info static void PrintCpuInfo(JSONWriter* writer) { - uv_cpu_info_t* cpu_info; - int count; - if (uv_cpu_info(&cpu_info, &count) == 0) { + CPUInfo cpu_info; + if (cpu_info) { writer->json_arraystart("cpus"); - for (int i = 0; i < count; i++) { + for (int i = 0; i < cpu_info.count(); i++) { writer->json_start(); writer->json_keyvalue("model", cpu_info[i].model); writer->json_keyvalue("speed", cpu_info[i].speed); @@ -403,7 +403,6 @@ static void PrintCpuInfo(JSONWriter* writer) { writer->json_end(); } writer->json_arrayend(); - uv_free_cpu_info(cpu_info, count); } } diff --git a/src/util.h b/src/util.h index 5eaa20b760168b..97818d85b0fced 100644 --- a/src/util.h +++ b/src/util.h @@ -33,6 +33,8 @@ #pragma GCC diagnostic pop #endif +#include "uv.h" + #include #include // PATH_MAX #include @@ -764,6 +766,31 @@ class PersistentToLocal { } }; +class CPUInfo { + public: + CPUInfo() { + if (uv_cpu_info(&info_, &count_) != 0) { + info_ = nullptr; + count_ = 0; + } + } + ~CPUInfo() { + if (info_ != nullptr) + uv_free_cpu_info(info_, count_); + } + int count() const { return count_; } + operator bool() const { + return info_ != nullptr; + } + const uv_cpu_info_t& operator[](int idx) const { + return info_[idx]; + } + + private: + uv_cpu_info_t* info_ = nullptr; + int count_ = 0; +}; + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From 4c94cd182a147fc850ea0c033ff2e26fe1eda31f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 1 Apr 2020 17:02:18 -0700 Subject: [PATCH 2/3] src: implement --max-worker-threads warning limit Creating too many active worker threads at one time can lead to significant performance degradation of the entire Node.js process. This adds a worker thread counter that will cause a warning to be emitted if exceeded. Workers can still be created beyond the limit, however. The warning is similar in spirit to the too many event handlers warning emitted by EventEmitter. By default, the limit is one less than four times the total number of CPUs available calculated at system start. The `--max-worker-threads` command-line option can be set to set a non-default value. The option is permitted in `NODE_OPTIONS` and must be positive number greater than zero. The counter and the option are per-process in order to account for Workers that create their own Workers. The warning will be emitted once each time the limit is exceeded, so may be emitted more than once per process. That is, if the limit is 2, and 5 workers are created, only a single warning will be emitted. If the number of active workers falls back below 2 and is subsequently exceeded again, the warning will be emitted again. Signed-off-by: James M Snell --- doc/api/cli.md | 11 +++++ doc/node.1 | 7 +++ src/node_options.cc | 16 +++++++ src/node_options.h | 3 ++ src/node_worker.cc | 46 +++++++++++++++++++ test/parallel/test-cli-bad-options.js | 1 + test/parallel/test-cli-node-options.js | 1 + test/parallel/test-worker-max-count-auto.js | 19 ++++++++ .../test-worker-max-count-disabled.js | 33 +++++++++++++ test/parallel/test-worker-max-count-nested.js | 40 ++++++++++++++++ test/parallel/test-worker-max-count.js | 46 +++++++++++++++++++ 11 files changed, 223 insertions(+) create mode 100644 test/parallel/test-worker-max-count-auto.js create mode 100644 test/parallel/test-worker-max-count-disabled.js create mode 100644 test/parallel/test-worker-max-count-nested.js create mode 100644 test/parallel/test-worker-max-count.js diff --git a/doc/api/cli.md b/doc/api/cli.md index 386d9cdb96ecb3..f057063a857f2b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -448,6 +448,17 @@ changes: Specify the maximum size, in bytes, of HTTP headers. Defaults to 16KB. +### `--max-worker-threads=n` + + +Specify the maximum number of worker threads that should be created for +this Node.js process. If the limit is exceeded, additional Worker threads +may be created but a process warning will be emitted. When set to any negative +value, the limit is set to four times the number of CPUs available. When set +to 0, the check is disabled. Defaults to 0. + ### `--napi-modules`