diff options
author | Anna Henningsen <anna@addaleax.net> | 2019-03-08 19:35:40 +0100 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2019-11-05 19:57:13 +0100 |
commit | d855904ef6a0daa0c0475e745fdd33815e760e0c (patch) | |
tree | 56c605726ad3200bd533a3bfab836c45ad48473e /lib | |
parent | 309e1eae797d3060126ef2e13ee0ab124808f575 (diff) | |
download | android-node-v8-d855904ef6a0daa0c0475e745fdd33815e760e0c.tar.gz android-node-v8-d855904ef6a0daa0c0475e745fdd33815e760e0c.tar.bz2 android-node-v8-d855904ef6a0daa0c0475e745fdd33815e760e0c.zip |
worker: allow specifying resource limits
Allow specifying resource limits for the JS engine instance created
as part of a Worker.
PR-URL: https://github.com/nodejs/node/pull/26628
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/errors.js | 2 | ||||
-rw-r--r-- | lib/internal/worker.js | 52 | ||||
-rw-r--r-- | lib/worker_threads.js | 2 |
3 files changed, 51 insertions, 5 deletions
diff --git a/lib/internal/errors.js b/lib/internal/errors.js index cd3c162183..2684931a77 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1226,6 +1226,8 @@ E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors) => `Initiated Worker with invalid execArgv flags: ${errors.join(', ')}`, Error); +E('ERR_WORKER_OUT_OF_MEMORY', 'Worker terminated due to reaching memory limit', + Error); E('ERR_WORKER_PATH', 'The worker script filename must be an absolute path or a relative ' + 'path starting with \'./\' or \'../\'. Received "%s"', diff --git a/lib/internal/worker.js b/lib/internal/worker.js index fc6588d527..614f930105 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -2,19 +2,20 @@ /* global SharedArrayBuffer */ -const { Object } = primordials; +const { Math, Object } = primordials; const EventEmitter = require('events'); const assert = require('internal/assert'); const path = require('path'); +const errorCodes = require('internal/errors').codes; const { ERR_WORKER_PATH, ERR_WORKER_UNSERIALIZABLE_ERROR, ERR_WORKER_UNSUPPORTED_EXTENSION, ERR_WORKER_INVALID_EXEC_ARGV, ERR_INVALID_ARG_TYPE, -} = require('internal/errors').codes; +} = errorCodes; const { validateString } = require('internal/validators'); const { getOptionValue } = require('internal/options'); @@ -37,8 +38,13 @@ const { pathToFileURL } = require('url'); const { ownsProcessState, isMainThread, + resourceLimits: resourceLimitsRaw, threadId, Worker: WorkerImpl, + kMaxYoungGenerationSizeMb, + kMaxOldGenerationSizeMb, + kCodeRangeSizeMb, + kTotalResourceLimitCount } = internalBinding('worker'); const kHandle = Symbol('kHandle'); @@ -102,7 +108,8 @@ class Worker extends EventEmitter { const url = options.eval ? null : pathToFileURL(filename); // Set up the C++ handle for the worker, as well as some internal wiring. - this[kHandle] = new WorkerImpl(url, options.execArgv); + this[kHandle] = new WorkerImpl(url, options.execArgv, + parseResourceLimits(options.resourceLimits)); if (this[kHandle].invalidExecArgv) { throw new ERR_WORKER_INVALID_EXEC_ARGV(this[kHandle].invalidExecArgv); } @@ -113,7 +120,7 @@ class Worker extends EventEmitter { } else if (env !== undefined) { this[kHandle].setEnvVars(env); } - this[kHandle].onexit = (code) => this[kOnExit](code); + this[kHandle].onexit = (code, customErr) => this[kOnExit](code, customErr); this[kPort] = this[kHandle].messagePort; this[kPort].on('message', (data) => this[kOnMessage](data)); this[kPort].start(); @@ -157,11 +164,15 @@ class Worker extends EventEmitter { this[kHandle].startThread(); } - [kOnExit](code) { + [kOnExit](code, customErr) { debug(`[${threadId}] hears end event for Worker ${this.threadId}`); drainMessagePort(this[kPublicPort]); drainMessagePort(this[kPort]); this[kDispose](); + if (customErr) { + debug(`[${threadId}] failing with custom error ${customErr}`); + this.emit('error', new errorCodes[customErr]()); + } this.emit('exit', code); this.removeAllListeners(); } @@ -280,6 +291,12 @@ class Worker extends EventEmitter { get stderr() { return this[kParentSideStdio].stderr; } + + get resourceLimits() { + if (this[kHandle] === null) return {}; + + return makeResourceLimits(this[kHandle].getResourceLimits()); + } } function pipeWithoutWarning(source, dest) { @@ -294,10 +311,35 @@ function pipeWithoutWarning(source, dest) { dest._maxListeners = destMaxListeners; } +const resourceLimitsArray = new Float64Array(kTotalResourceLimitCount); +function parseResourceLimits(obj) { + const ret = resourceLimitsArray; + ret.fill(-1); + if (typeof obj !== 'object' || obj === null) return ret; + + if (typeof obj.maxOldGenerationSizeMb === 'number') + ret[kMaxOldGenerationSizeMb] = Math.max(obj.maxOldGenerationSizeMb, 2); + if (typeof obj.maxYoungGenerationSizeMb === 'number') + ret[kMaxYoungGenerationSizeMb] = obj.maxYoungGenerationSizeMb; + if (typeof obj.codeRangeSizeMb === 'number') + ret[kCodeRangeSizeMb] = obj.codeRangeSizeMb; + return ret; +} + +function makeResourceLimits(float64arr) { + return { + maxYoungGenerationSizeMb: float64arr[kMaxYoungGenerationSizeMb], + maxOldGenerationSizeMb: float64arr[kMaxOldGenerationSizeMb], + codeRangeSizeMb: float64arr[kCodeRangeSizeMb] + }; +} + module.exports = { ownsProcessState, isMainThread, SHARE_ENV, + resourceLimits: + !isMainThread ? makeResourceLimits(resourceLimitsRaw) : {}, threadId, Worker, }; diff --git a/lib/worker_threads.js b/lib/worker_threads.js index bd455edea2..4b72bf2711 100644 --- a/lib/worker_threads.js +++ b/lib/worker_threads.js @@ -3,6 +3,7 @@ const { isMainThread, SHARE_ENV, + resourceLimits, threadId, Worker } = require('internal/worker'); @@ -20,6 +21,7 @@ module.exports = { MessageChannel, moveMessagePortToContext, receiveMessageOnPort, + resourceLimits, threadId, SHARE_ENV, Worker, |