From ea86656c08258a608d4caa18338a9930b62da31b Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 7 Feb 2018 01:36:20 +0100 Subject: [PATCH 01/47] net: use `_final` instead of `on('finish')` Shutting down the connection is what `_final` is there for. PR-URL: https://github.com/nodejs/node/pull/18608 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- lib/internal/streams/destroy.js | 2 ++ lib/net.js | 28 +++++++++++-------- test/async-hooks/test-shutdownwrap.js | 12 ++++---- test/parallel/test-stream-writable-destroy.js | 14 ++++++++++ 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 86a22633f2fe78..985332ac4607a8 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -55,6 +55,8 @@ function undestroy() { this._writableState.destroyed = false; this._writableState.ended = false; this._writableState.ending = false; + this._writableState.finalCalled = false; + this._writableState.prefinished = false; this._writableState.finished = false; this._writableState.errorEmitted = false; } diff --git a/lib/net.js b/lib/net.js index c04d04473900d0..35d252ceaa48cd 100644 --- a/lib/net.js +++ b/lib/net.js @@ -236,7 +236,6 @@ function Socket(options) { } // shut down the socket when we're finished with it. - this.on('finish', onSocketFinish); this.on('_socketEnd', onSocketEnd); initSocketHandle(this); @@ -280,39 +279,42 @@ Socket.prototype._unrefTimer = function _unrefTimer() { function shutdownSocket(self, callback) { var req = new ShutdownWrap(); - req.oncomplete = callback; + req.oncomplete = afterShutdown; req.handle = self._handle; + req.callback = callback; return self._handle.shutdown(req); } // the user has called .end(), and all the bytes have been // sent out to the other side. -function onSocketFinish() { - // If still connecting - defer handling 'finish' until 'connect' will happen +Socket.prototype._final = function(cb) { + // If still connecting - defer handling `_final` until 'connect' will happen if (this.connecting) { - debug('osF: not yet connected'); - return this.once('connect', onSocketFinish); + debug('_final: not yet connected'); + return this.once('connect', () => this._final(cb)); } - debug('onSocketFinish'); if (!this.readable || this._readableState.ended) { - debug('oSF: ended, destroy', this._readableState); + debug('_final: ended, destroy', this._readableState); + cb(); return this.destroy(); } - debug('oSF: not ended, call shutdown()'); + debug('_final: not ended, call shutdown()'); // otherwise, just shutdown, or destroy() if not possible - if (!this._handle || !this._handle.shutdown) + if (!this._handle || !this._handle.shutdown) { + cb(); return this.destroy(); + } var err = defaultTriggerAsyncIdScope( - this[async_id_symbol], shutdownSocket, this, afterShutdown + this[async_id_symbol], shutdownSocket, this, cb ); if (err) return this.destroy(errnoException(err, 'shutdown')); -} +}; function afterShutdown(status, handle, req) { @@ -321,6 +323,8 @@ function afterShutdown(status, handle, req) { debug('afterShutdown destroyed=%j', self.destroyed, self._readableState); + this.callback(); + // callback may come after call to destroy. if (self.destroyed) return; diff --git a/test/async-hooks/test-shutdownwrap.js b/test/async-hooks/test-shutdownwrap.js index dfaac2a1c05a70..fea4a3a166a270 100644 --- a/test/async-hooks/test-shutdownwrap.js +++ b/test/async-hooks/test-shutdownwrap.js @@ -24,11 +24,13 @@ let endedConnection = false; function onconnection(c) { assert.strictEqual(hooks.activitiesOfTypes('SHUTDOWNWRAP').length, 0); c.end(); - endedConnection = true; - const as = hooks.activitiesOfTypes('SHUTDOWNWRAP'); - assert.strictEqual(as.length, 1); - checkInvocations(as[0], { init: 1 }, 'after ending client connection'); - this.close(onserverClosed); + process.nextTick(() => { + endedConnection = true; + const as = hooks.activitiesOfTypes('SHUTDOWNWRAP'); + assert.strictEqual(as.length, 1); + checkInvocations(as[0], { init: 1 }, 'after ending client connection'); + this.close(onserverClosed); + }); } function onconnected() { diff --git a/test/parallel/test-stream-writable-destroy.js b/test/parallel/test-stream-writable-destroy.js index 87e55eccc3f6fd..46c48511177813 100644 --- a/test/parallel/test-stream-writable-destroy.js +++ b/test/parallel/test-stream-writable-destroy.js @@ -185,3 +185,17 @@ const { inherits } = require('util'); assert.strictEqual(expected, err); })); } + +{ + // Checks that `._undestroy()` restores the state so that `final` will be + // called again. + const write = new Writable({ + write: common.mustNotCall(), + final: common.mustCall((cb) => cb(), 2) + }); + + write.end(); + write.destroy(); + write._undestroy(); + write.end(); +} From 1fd4e4d6a5f123e8531f3f745c0126ea676d70fb Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Tue, 20 Feb 2018 05:21:05 +0100 Subject: [PATCH 02/47] src: fix deprecation warning in node_perf.cc Currently the following deprecation warning is produced when compiling node_perf.cc: ./src/node_perf.cc:91:11: warning: 'MakeCallback' is deprecated: Use MakeCallback(..., async_context) [-Wdeprecated-declarations] node::MakeCallback(env->isolate(), ^ ../src/node.h:172:50: note: 'MakeCallback' has been explicitly marked deprecated here NODE_EXTERN v8::Local MakeCallback( ^ 1 warning generated. This commit adds an async_context to the call and checks the maybe result. PR-URL: https://github.com/nodejs/node/pull/18877 Reviewed-By: Ali Ijaz Sheikh --- src/node_perf.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node_perf.cc b/src/node_perf.cc index 38dbdaca5adce1..8862e5fab755a1 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -91,7 +91,8 @@ void PerformanceEntry::Notify(Environment* env, node::MakeCallback(env->isolate(), env->process_object(), env->performance_entry_callback(), - 1, &object); + 1, &object, + node::async_context{0, 0}).ToLocalChecked(); } } From 15c9a7154342153a0cc083d07528dd7a7e779e5f Mon Sep 17 00:00:00 2001 From: Roman Reiss Date: Wed, 21 Feb 2018 19:38:36 +0100 Subject: [PATCH 03/47] doc: remove CII badge in README PR-URL: https://github.com/nodejs/node/pull/18908 Reviewed-By: Rod Vagg Reviewed-By: Rich Trott Reviewed-By: Jeremiah Senkpiel Reviewed-By: Daijiro Wachi Reviewed-By: Colin Ihrig Reviewed-By: Gus Caplan Reviewed-By: Ruben Bridgewater --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 687f0fb4723862..9dda47e821fc68 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,6 @@ Node.js

-

- -

Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. For more information on using Node.js, see the From dfe0bc13408056f5b8f3bca8558f3e6750859721 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Sat, 10 Feb 2018 14:52:18 +0100 Subject: [PATCH 04/47] cluster: fix inspector port assignment Make sure that inspector ports in cluster are inside the valid range: `[1024, 65535]`. Fixes flaky `test-inspector-port-zero-cluster`. PR-URL: https://github.com/nodejs/node/pull/18696 Fixes: https://github.com/nodejs/node/issues/18303 Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- lib/internal/cluster/master.js | 3 +++ test/sequential/test-inspector-port-cluster.js | 10 ++++++++++ test/sequential/test-inspector-port-zero-cluster.js | 10 ++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 570cf7bc6f93d4..3c6a398f117433 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -14,6 +14,7 @@ const intercom = new EventEmitter(); const SCHED_NONE = 1; const SCHED_RR = 2; const { isLegalPort } = require('internal/net'); +const [ minPort, maxPort ] = [ 1024, 65535 ]; module.exports = cluster; @@ -119,6 +120,8 @@ function createWorkerProcess(id, env) { } } else { inspectPort = process.debugPort + debugPortOffset; + if (inspectPort > maxPort) + inspectPort = inspectPort - maxPort + minPort - 1; debugPortOffset++; } diff --git a/test/sequential/test-inspector-port-cluster.js b/test/sequential/test-inspector-port-cluster.js index 84ec408ebfb178..87469aa7ff77c5 100644 --- a/test/sequential/test-inspector-port-cluster.js +++ b/test/sequential/test-inspector-port-cluster.js @@ -24,6 +24,16 @@ function testRunnerMain() { workers: [{ expectedPort: 9230 }] }); + spawnMaster({ + execArgv: ['--inspect=65534'], + workers: [ + { expectedPort: 65535 }, + { expectedPort: 1024 }, + { expectedPort: 1025 }, + { expectedPort: 1026 } + ] + }); + let port = debuggerPort + offset++ * 5; spawnMaster({ diff --git a/test/sequential/test-inspector-port-zero-cluster.js b/test/sequential/test-inspector-port-zero-cluster.js index f64e05f314c0c6..e522056571d1c2 100644 --- a/test/sequential/test-inspector-port-zero-cluster.js +++ b/test/sequential/test-inspector-port-zero-cluster.js @@ -30,16 +30,14 @@ function serialFork() { if (cluster.isMaster) { Promise.all([serialFork(), serialFork(), serialFork()]) .then(common.mustCall((ports) => { - ports.push(process.debugPort); - ports.sort(); + ports.splice(0, 0, process.debugPort); // 4 = [master, worker1, worker2, worker3].length() assert.strictEqual(ports.length, 4); assert(ports.every((port) => port > 0)); assert(ports.every((port) => port < 65536)); - // Ports should be consecutive. - assert.strictEqual(ports[0] + 1, ports[1]); - assert.strictEqual(ports[1] + 1, ports[2]); - assert.strictEqual(ports[2] + 1, ports[3]); + assert.strictEqual(ports[0] === 65535 ? 1024 : ports[0] + 1, ports[1]); + assert.strictEqual(ports[1] === 65535 ? 1024 : ports[1] + 1, ports[2]); + assert.strictEqual(ports[2] === 65535 ? 1024 : ports[2] + 1, ports[3]); })) .catch( (err) => { From 9d73910863559c19bf3f6bb07f93006725e23e19 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 6 Feb 2018 23:00:15 +0100 Subject: [PATCH 05/47] net: inline and simplify onSocketEnd PR-URL: https://github.com/nodejs/node/pull/18607 Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina Reviewed-By: James M Snell --- lib/net.js | 41 ++++++++---------------------- test/parallel/test-http-connect.js | 3 ++- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/lib/net.js b/lib/net.js index 35d252ceaa48cd..f291e0555bdbc3 100644 --- a/lib/net.js +++ b/lib/net.js @@ -236,7 +236,7 @@ function Socket(options) { } // shut down the socket when we're finished with it. - this.on('_socketEnd', onSocketEnd); + this.on('end', onReadableStreamEnd); initSocketHandle(this); @@ -337,32 +337,6 @@ function afterShutdown(status, handle, req) { } } -// the EOF has been received, and no more bytes are coming. -// if the writable side has ended already, then clean everything -// up. -function onSocketEnd() { - // XXX Should not have to do as much in this function. - // ended should already be true, since this is called *after* - // the EOF errno and onread has eof'ed - debug('onSocketEnd', this._readableState); - this._readableState.ended = true; - if (this._readableState.endEmitted) { - this.readable = false; - maybeDestroy(this); - } else { - this.once('end', function end() { - this.readable = false; - maybeDestroy(this); - }); - this.read(0); - } - - if (!this.allowHalfOpen) { - this.write = writeAfterFIN; - this.destroySoon(); - } -} - // Provide a better error message when we call end() as a result // of the other side sending a FIN. The standard 'write after end' // is overly vague, and makes it seem like the user's code is to blame. @@ -508,6 +482,12 @@ Socket.prototype.end = function(data, encoding, callback) { }; +// Called when the 'end' event is emitted. +function onReadableStreamEnd() { + maybeDestroy(this); +} + + // Call whenever we set writable=false or readable=false function maybeDestroy(socket) { if (!socket.readable && @@ -621,10 +601,11 @@ function onread(nread, buffer) { // Do it before `maybeDestroy` for correct order of events: // `end` -> `close` self.push(null); + self.read(0); - if (self.readableLength === 0) { - self.readable = false; - maybeDestroy(self); + if (!self.allowHalfOpen) { + self.write = writeAfterFIN; + self.destroySoon(); } // internal end event so that we know that the actual socket diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index b019c61573e3fa..06f855db9a0702 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -65,7 +65,8 @@ server.listen(0, common.mustCall(() => { // the stream.Duplex onend listener // allow 0 here, so that i can run the same test on streams1 impl - assert(socket.listeners('end').length <= 1); + assert(socket.listenerCount('end') <= 2, + `Found ${socket.listenerCount('end')} end listeners`); assert.strictEqual(socket.listeners('free').length, 0); assert.strictEqual(socket.listeners('close').length, 0); From ad98ff159383167d8059c23036f3e3e8a4f09f7e Mon Sep 17 00:00:00 2001 From: "Timothy O. Peters" Date: Mon, 12 Feb 2018 19:58:15 +0100 Subject: [PATCH 06/47] vm: consolidate validation PR-URL: https://github.com/nodejs/node/pull/18816 Reviewed-By: Ruben Bridgewater Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Luigi Pinca --- lib/vm.js | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/vm.js b/lib/vm.js index ecb675680e95d9..4da0d7c6c3c528 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -80,22 +80,23 @@ Script.prototype.runInNewContext = function(sandbox, options) { return this.runInContext(context, options); }; +function validateString(prop, propName) { + if (prop !== undefined && typeof prop !== 'string') + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', propName, + 'string', prop); +} + function getContextOptions(options) { - const contextOptions = options ? { - name: options.contextName, - origin: options.contextOrigin - } : {}; - if (contextOptions.name !== undefined && - typeof contextOptions.name !== 'string') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options.contextName', - 'string', contextOptions.name); - } - if (contextOptions.origin !== undefined && - typeof contextOptions.origin !== 'string') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options.contextOrigin', - 'string', contextOptions.origin); + if (options) { + const contextOptions = { + name: options.contextName, + origin: options.contextOrigin + }; + validateString(contextOptions.name, 'options.contextName'); + validateString(contextOptions.origin, 'options.contextOrigin'); + return contextOptions; } - return contextOptions; + return {}; } let defaultContextNameIndex = 1; @@ -121,10 +122,7 @@ function createContext(sandbox, options) { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options.name', 'string', options.name); } - if (options.origin !== undefined && typeof options.origin !== 'string') { - throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'options.origin', - 'string', options.origin); - } + validateString(options.origin, 'options.origin'); } else { options = { name: `VM Context ${defaultContextNameIndex++}` From e29acf8fa40ab7d04813dbe0ee305b506b80d8e9 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Sun, 18 Feb 2018 20:19:20 +0100 Subject: [PATCH 07/47] tools: fix custom eslint rule errors This fixes a few rules by making sure the input is actually ready to be checked. Otherwise those can throw TypeErrors or result in faulty error messages. PR-URL: https://github.com/nodejs/node/pull/18853 Reviewed-By: Luigi Pinca --- tools/eslint-rules/alphabetize-errors.js | 9 ++------- tools/eslint-rules/documented-errors.js | 10 ++-------- tools/eslint-rules/prefer-util-format-errors.js | 11 +++-------- tools/eslint-rules/rules-utils.js | 8 ++++++++ 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/tools/eslint-rules/alphabetize-errors.js b/tools/eslint-rules/alphabetize-errors.js index 2f604130e995b4..b2dcacbb866816 100644 --- a/tools/eslint-rules/alphabetize-errors.js +++ b/tools/eslint-rules/alphabetize-errors.js @@ -1,5 +1,7 @@ 'use strict'; +const { isDefiningError } = require('./rules-utils.js'); + const prefix = 'Out of ASCIIbetical order - '; const opStr = ' >= '; @@ -7,13 +9,6 @@ function errorForNode(node) { return node.expression.arguments[0].value; } -function isDefiningError(node) { - return node.expression && - node.expression.type === 'CallExpression' && - node.expression.callee && - node.expression.callee.name === 'E'; -} - module.exports = { create: function(context) { let previousNode; diff --git a/tools/eslint-rules/documented-errors.js b/tools/eslint-rules/documented-errors.js index 471452d67a04f2..0dbabb0673aa4b 100644 --- a/tools/eslint-rules/documented-errors.js +++ b/tools/eslint-rules/documented-errors.js @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); +const { isDefiningError } = require('./rules-utils.js'); const doc = fs.readFileSync(path.resolve(__dirname, '../../doc/api/errors.md'), 'utf8'); @@ -18,18 +19,11 @@ function errorForNode(node) { return node.expression.arguments[0].value; } -function isDefiningError(node) { - return node.expression && - node.expression.type === 'CallExpression' && - node.expression.callee && - node.expression.callee.name === 'E'; -} - module.exports = { create: function(context) { return { ExpressionStatement: function(node) { - if (!isDefiningError(node)) return; + if (!isDefiningError(node) || !errorForNode(node)) return; const code = errorForNode(node); if (!isInDoc(code)) { const message = `"${code}" is not documented in doc/api/errors.md`; diff --git a/tools/eslint-rules/prefer-util-format-errors.js b/tools/eslint-rules/prefer-util-format-errors.js index f6993e627782dd..407b6e20dd23ea 100644 --- a/tools/eslint-rules/prefer-util-format-errors.js +++ b/tools/eslint-rules/prefer-util-format-errors.js @@ -1,5 +1,7 @@ 'use strict'; +const { isDefiningError } = require('./rules-utils.js'); + const errMsg = 'Please use a printf-like formatted string that util.format' + ' can consume.'; @@ -8,18 +10,11 @@ function isArrowFunctionWithTemplateLiteral(node) { node.body.type === 'TemplateLiteral'; } -function isDefiningError(node) { - return node.expression && - node.expression.type === 'CallExpression' && - node.expression.callee && - node.expression.callee.name === 'E'; -} - module.exports = { create: function(context) { return { ExpressionStatement: function(node) { - if (!isDefiningError(node)) + if (!isDefiningError(node) || node.expression.arguments.length < 2) return; const msg = node.expression.arguments[1]; diff --git a/tools/eslint-rules/rules-utils.js b/tools/eslint-rules/rules-utils.js index 88ecf658ce37f0..0e89a715450edb 100644 --- a/tools/eslint-rules/rules-utils.js +++ b/tools/eslint-rules/rules-utils.js @@ -3,6 +3,14 @@ */ 'use strict'; +module.exports.isDefiningError = function(node) { + return node.expression && + node.expression.type === 'CallExpression' && + node.expression.callee && + node.expression.callee.name === 'E' && + node.expression.arguments.length !== 0; +}; + /** * Returns true if any of the passed in modules are used in * require calls. From 04f0482a7d6bc98256d03afdb1cf05c6534d2031 Mon Sep 17 00:00:00 2001 From: Bartosz Sosnowski Date: Mon, 13 Nov 2017 16:59:11 +0100 Subject: [PATCH 08/47] build, win: vcbuild improvements Removes extra erroor messages when Python is not installed. Removes "vswhere not found" message when no VS2017 installation is found. Adds support for DEBUG_HELPER to vcbuild.bat. Fixes: https://github.com/nodejs/node/issues/16864 PR-URL: https://github.com/nodejs/node/pull/17015 Reviewed-By: Ben Noordhuis Reviewed-By: Gibson Fahnestock --- tools/msvs/find_python.cmd | 20 ++++++++++++++------ tools/msvs/vswhere_usability_wrapper.cmd | 1 - vcbuild.bat | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/tools/msvs/find_python.cmd b/tools/msvs/find_python.cmd index f2a779290e8104..212b5275c318ae 100644 --- a/tools/msvs/find_python.cmd +++ b/tools/msvs/find_python.cmd @@ -1,4 +1,5 @@ @IF NOT DEFINED DEBUG_HELPER @ECHO OFF +echo Looking for Python 2.x SETLOCAL :: If python.exe is in %Path%, just validate FOR /F "delims=" %%a IN ('where python 2^> NUL') DO ( @@ -14,18 +15,18 @@ FOR %%K IN ( "HKCU\Software", "HKLM\SOFTWARE", "HKLM\Software\Wow6432Node") DO ( :: If validate returns 0 just jump to the end IF NOT ERRORLEVEL 1 GOTO :validate ) -EXIT /B 1 +goto :no-python :: Helper subroutine to handle quotes in %1 :find-main-branch SET main_key="%~1\Python\PythonCore" -REG QUERY %main_key% /s | findstr "2." | findstr InstallPath > NUL 2> NUL +REG QUERY %main_key% /s 2> NUL | findstr "2." | findstr InstallPath > NUL 2> NUL IF NOT ERRORLEVEL 1 CALL :find-key %main_key% EXIT /B :: Query registry sub-tree for InstallPath :find-key -FOR /F "delims=" %%a IN ('REG QUERY %1 /s ^| findstr "2." ^| findstr InstallPath') DO IF NOT ERRORLEVEL 1 CALL :find-path %%a +FOR /F "delims=" %%a IN ('REG QUERY %1 /s 2> NUL ^| findstr "2." ^| findstr InstallPath') DO IF NOT ERRORLEVEL 1 CALL :find-path %%a EXIT /B :: Parse the value of %1 as the path for python.exe @@ -39,13 +40,20 @@ EXIT /B 1 :: Check if %p% holds a path to a real python2 executable :validate -IF NOT EXIST "%p%python.exe" EXIT /B 1 +IF NOT EXIST "%p%python.exe" goto :no-python :: Check if %p% is python2 "%p%python.exe" -V 2>&1 | findstr /R "^Python.2.*" > NUL -IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL% +IF ERRORLEVEL 1 goto :no-python2 :: We can wrap it up ENDLOCAL & SET pt=%p%& SET need_path_ext=%need_path% SET VCBUILD_PYTHON_LOCATION=%pt%python.exe IF %need_path_ext%==1 SET Path=%Path%;%pt% SET need_path_ext= -EXIT /B %ERRORLEVEL% \ No newline at end of file +EXIT /B %ERRORLEVEL% + +:no-python2 +echo Python found in %p%, but it is not v2.x. +exit /B 1 +:no-python +echo Could not find Python. +exit /B 1 diff --git a/tools/msvs/vswhere_usability_wrapper.cmd b/tools/msvs/vswhere_usability_wrapper.cmd index a260a50a2c2dda..4a159baabc0bfb 100644 --- a/tools/msvs/vswhere_usability_wrapper.cmd +++ b/tools/msvs/vswhere_usability_wrapper.cmd @@ -28,5 +28,4 @@ for /f "usebackq tokens=*" %%i in (`vswhere %VSWHERE_ARGS%`) do ( :no-vswhere endlocal -echo could not find "vswhere" exit /B 1 diff --git a/vcbuild.bat b/vcbuild.bat index 8758fb826138d6..9d5bff7df720ab 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -1,4 +1,4 @@ -@echo off +@if not defined DEBUG_HELPER @ECHO OFF cd %~dp0 @@ -170,7 +170,7 @@ if "%target%"=="Clean" rmdir /S /Q %~dp0deps\icu :no-depsicu call tools\msvs\find_python.cmd -if errorlevel 1 echo Could not find python2 & goto :exit +if errorlevel 1 goto :exit call :getnodeversion || exit /b 1 From e3f901c864f77a0b8b77e46e30f9b3e6b451d367 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Mon, 19 Feb 2018 14:43:02 +0100 Subject: [PATCH 09/47] http: allow _httpMessage to be GC'ed Set `socket._httpMessage` to `null` before emitting the `'connect'` or `'upgrade'` event. PR-URL: https://github.com/nodejs/node/pull/18865 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Matteo Collina --- lib/_http_agent.js | 1 + test/parallel/test-http-connect.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 5f1e56caeab981..7586a48680bb6a 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -277,6 +277,7 @@ function installListeners(agent, s, options) { s.removeListener('close', onClose); s.removeListener('free', onFree); s.removeListener('agentRemove', onRemove); + s._httpMessage = null; } s.on('agentRemove', onRemove); } diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index 06f855db9a0702..30668ec9937abe 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -49,6 +49,10 @@ server.listen(0, common.mustCall(() => { path: 'google.com:443' }, common.mustNotCall()); + req.on('socket', common.mustCall((socket) => { + assert.strictEqual(socket._httpMessage, req); + })); + req.on('close', common.mustCall()); req.on('connect', common.mustCall((res, socket, firstBodyChunk) => { @@ -60,6 +64,7 @@ server.listen(0, common.mustCall(() => { // Make sure this socket has detached. assert(!socket.ondata); assert(!socket.onend); + assert.strictEqual(socket._httpMessage, null); assert.strictEqual(socket.listeners('connect').length, 0); assert.strictEqual(socket.listeners('data').length, 0); From 2ad7cb9b410b1ce2593ed6093a3f9ba57c9867de Mon Sep 17 00:00:00 2001 From: Trivikram <16024985+trivikr@users.noreply.github.com> Date: Sun, 18 Feb 2018 13:39:06 -0800 Subject: [PATCH 10/47] test: http2 compat response.write() error checks PR-URL: https://github.com/nodejs/node/pull/18859 Reviewed-By: James M Snell --- ...http2-compat-serverresponse-write-no-cb.js | 95 ------------------- .../test-http2-compat-serverresponse-write.js | 52 ++++++++++ 2 files changed, 52 insertions(+), 95 deletions(-) delete mode 100644 test/parallel/test-http2-compat-serverresponse-write-no-cb.js create mode 100644 test/parallel/test-http2-compat-serverresponse-write.js diff --git a/test/parallel/test-http2-compat-serverresponse-write-no-cb.js b/test/parallel/test-http2-compat-serverresponse-write-no-cb.js deleted file mode 100644 index a62bb1b0ac78f1..00000000000000 --- a/test/parallel/test-http2-compat-serverresponse-write-no-cb.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -const { mustCall, - mustNotCall, - expectsError, - hasCrypto, skip } = require('../common'); -if (!hasCrypto) - skip('missing crypto'); -const { createServer, connect } = require('http2'); - -// Http2ServerResponse.write does not imply there is a callback - -{ - const server = createServer(); - server.listen(0, mustCall(() => { - const port = server.address().port; - const url = `http://localhost:${port}`; - const client = connect(url, mustCall(() => { - const request = client.request(); - request.resume(); - request.on('end', mustCall()); - request.on('close', mustCall(() => { - client.close(); - })); - })); - - server.once('request', mustCall((request, response) => { - client.destroy(); - response.stream.session.on('close', mustCall(() => { - response.on('error', mustNotCall()); - expectsError( - () => { response.write('muahaha'); }, - { - code: 'ERR_HTTP2_INVALID_STREAM' - } - ); - server.close(); - })); - })); - })); -} - -{ - const server = createServer(); - server.listen(0, mustCall(() => { - const port = server.address().port; - const url = `http://localhost:${port}`; - const client = connect(url, mustCall(() => { - const request = client.request(); - request.resume(); - request.on('end', mustCall()); - request.on('close', mustCall(() => client.close())); - })); - - server.once('request', mustCall((request, response) => { - client.destroy(); - response.stream.session.on('close', mustCall(() => { - expectsError( - () => response.write('muahaha'), - { - code: 'ERR_HTTP2_INVALID_STREAM' - } - ); - server.close(); - })); - })); - })); -} - -{ - const server = createServer(); - server.listen(0, mustCall(() => { - const port = server.address().port; - const url = `http://localhost:${port}`; - const client = connect(url, mustCall(() => { - const request = client.request(); - request.resume(); - request.on('end', mustCall()); - request.on('close', mustCall(() => client.close())); - })); - - server.once('request', mustCall((request, response) => { - response.stream.session.on('close', mustCall(() => { - expectsError( - () => response.write('muahaha', 'utf8'), - { - code: 'ERR_HTTP2_INVALID_STREAM' - } - ); - server.close(); - })); - client.destroy(); - })); - })); -} diff --git a/test/parallel/test-http2-compat-serverresponse-write.js b/test/parallel/test-http2-compat-serverresponse-write.js new file mode 100644 index 00000000000000..af3029835eaecb --- /dev/null +++ b/test/parallel/test-http2-compat-serverresponse-write.js @@ -0,0 +1,52 @@ +'use strict'; + +const { + mustCall, + mustNotCall, + expectsError, + hasCrypto, + skip +} = require('../common'); +if (!hasCrypto) + skip('missing crypto'); +const { createServer, connect } = require('http2'); +const assert = require('assert'); + +const server = createServer(); +server.listen(0, mustCall(() => { + const port = server.address().port; + const url = `http://localhost:${port}`; + const client = connect(url, mustCall(() => { + const request = client.request(); + request.resume(); + request.on('end', mustCall()); + request.on('close', mustCall(() => { + client.close(); + })); + })); + + server.once('request', mustCall((request, response) => { + // response.write() returns true + assert(response.write('muahaha', 'utf8', mustCall())); + + response.stream.close(0, mustCall(() => { + response.on('error', mustNotCall()); + + // response.write() without cb returns error + expectsError( + () => { response.write('muahaha'); }, + { + type: Error, + code: 'ERR_HTTP2_INVALID_STREAM', + message: 'The stream has been destroyed' + } + ); + + // response.write() with cb returns falsy value + assert(!response.write('muahaha', mustCall())); + + client.destroy(); + server.close(); + })); + })); +})); From f6e2ba4b3c7d0789d79fabd81c24342c5dbcba85 Mon Sep 17 00:00:00 2001 From: killagu Date: Tue, 20 Feb 2018 20:12:58 +0800 Subject: [PATCH 11/47] repl: fix tab-complete warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When create a nest repl, will register `Runtime.executionContextCreated` listener to the inspector session.This patch will fix listener repeatedly register. PR-URL: https://github.com/nodejs/node/pull/18881 Fixes: https://github.com/nodejs/node/issues/18284 Reviewed-By: MichaĆ«l Zasso Reviewed-By: Anna Henningsen Reviewed-By: Benjamin Gruenbaum Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- lib/repl.js | 3 +-- .../test-repl-tab-complete-no-warn.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-repl-tab-complete-no-warn.js diff --git a/lib/repl.js b/lib/repl.js index ef62753134db6d..b7af18ed492a9f 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -650,7 +650,7 @@ REPLServer.prototype.createContext = function() { } else { sendInspectorCommand((session) => { session.post('Runtime.enable'); - session.on('Runtime.executionContextCreated', ({ params }) => { + session.once('Runtime.executionContextCreated', ({ params }) => { this[kContextId] = params.context.id; }); context = vm.createContext(); @@ -834,7 +834,6 @@ function complete(line, callback) { var flat = new ArrayStream(); // make a new "input" stream var magic = new REPLServer('', flat); // make a nested REPL replMap.set(magic, replMap.get(this)); - magic.resetContext(); flat.run(tmp); // eval the flattened code // all this is only profitable if the nested REPL // does not have a bufferedCommand diff --git a/test/parallel/test-repl-tab-complete-no-warn.js b/test/parallel/test-repl-tab-complete-no-warn.js new file mode 100644 index 00000000000000..3379cec8453cba --- /dev/null +++ b/test/parallel/test-repl-tab-complete-no-warn.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const repl = require('repl'); +const DEFAULT_MAX_LISTENERS = require('events').defaultMaxListeners; + +common.ArrayStream.prototype.write = () => { +}; + +const putIn = new common.ArrayStream(); +const testMe = repl.start('', putIn); + +// https://github.com/nodejs/node/issues/18284 +// Tab-completion should not repeatedly add the +// `Runtime.executionContextCreated` listener +process.on('warning', common.mustNotCall()); + +putIn.run(['.clear']); +putIn.run(['async function test() {']); +for (let i = 0; i < DEFAULT_MAX_LISTENERS; i++) { + testMe.complete('await Promise.resolve()', () => {}); +} From 8773c102f86fd62c7c3956cef41443557ed3be1d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 21 Feb 2018 15:49:47 +0100 Subject: [PATCH 12/47] src: fix abort when taking a heap snapshot Remove an erroneous CHECK that asserted the persistent object's internal field pointer still pointed to a valid object. If ClearWrap() has been called, the field pointer equals nullptr and that is expected behavior. PR-URL: https://github.com/nodejs/node/pull/18898 Fixes: https://github.com/nodejs/node/issues/18256 Reviewed-By: Colin Ihrig Reviewed-By: Joyee Cheung Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen Reviewed-By: Matheus Marchini --- src/async_wrap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async_wrap.cc b/src/async_wrap.cc index c9dfc40c0ca52d..5e9dc69f87839f 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -128,7 +128,7 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { CHECK_GT(object->InternalFieldCount(), 0); AsyncWrap* wrap = Unwrap(object); - CHECK_NE(nullptr, wrap); + if (wrap == nullptr) return nullptr; // ClearWrap() already called. return new RetainedAsyncInfo(class_id, wrap); } From 4bce02abb56c56a4e51b38c3ab622161021fbd63 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Mon, 19 Feb 2018 15:09:48 +0100 Subject: [PATCH 13/47] http: remove default 'drain' listener on upgrade Ensure that the default `'drain'` listener is removed before the `'connect'` or `'upgrade'` event is emitted. PR-URL: https://github.com/nodejs/node/pull/18866 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- lib/_http_client.js | 3 ++- test/parallel/test-http-connect.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/_http_client.js b/lib/_http_client.js index 62eef956b3574e..63efb06b8f7f9c 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -37,7 +37,7 @@ const { OutgoingMessage } = require('_http_outgoing'); const Agent = require('_http_agent'); const { Buffer } = require('buffer'); const { urlToOptions, searchParamsSymbol } = require('internal/url'); -const { outHeadersKey } = require('internal/http'); +const { outHeadersKey, ondrain } = require('internal/http'); const { nextTick } = require('internal/process/next_tick'); const errors = require('internal/errors'); @@ -456,6 +456,7 @@ function socketOnData(d) { socket.removeListener('data', socketOnData); socket.removeListener('end', socketOnEnd); + socket.removeListener('drain', ondrain); parser.finish(); freeParser(parser, req, socket); diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index 30668ec9937abe..9b7432f03a7542 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -67,6 +67,7 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(socket._httpMessage, null); assert.strictEqual(socket.listeners('connect').length, 0); assert.strictEqual(socket.listeners('data').length, 0); + assert.strictEqual(socket.listeners('drain').length, 0); // the stream.Duplex onend listener // allow 0 here, so that i can run the same test on streams1 impl From b4e13b0e40330b63f4fb8c93c3abf9f81e81aad3 Mon Sep 17 00:00:00 2001 From: Justin Lee Date: Mon, 19 Feb 2018 21:55:35 -0800 Subject: [PATCH 14/47] doc: fix link in onboarding.md Remove link to the outdated members team PR-URL: https://github.com/nodejs/node/pull/18878 Reviewed-By: Rich Trott Reviewed-By: Luigi Pinca Reviewed-By: Matheus Marchini --- doc/onboarding.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/onboarding.md b/doc/onboarding.md index 7f262e26665906..352a181f4dce8e 100644 --- a/doc/onboarding.md +++ b/doc/onboarding.md @@ -15,12 +15,10 @@ onboarding session. ## Fifteen minutes before the onboarding session * Prior to the onboarding session, add the new Collaborator to - [the Collaborators team](https://github.com/orgs/nodejs/teams/collaborators), - and to [the Members team](https://github.com/orgs/nodejs/teams/members) if - they are not already part of it. Note that this is the step that gives the - account elevated privileges, so do not perform this step (or any subsequent - steps) unless two-factor authentication is enabled on the new Collaborator's - GitHub account. + [the Collaborators team](https://github.com/orgs/nodejs/teams/collaborators). + Note that this is the step that gives the account elevated privileges, so do + not perform this step (or any subsequent steps) unless two-factor + authentication is enabled on the new Collaborator's GitHub account. ## Onboarding session From 8d0cc17b6eb8e1228b922d74707059a4993662dc Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Tue, 20 Feb 2018 18:33:05 +0100 Subject: [PATCH 15/47] doc: update description of 'clientError' event Default behavior is to send a '400 Bad Request' response if the socket is writable. PR-URL: https://github.com/nodejs/node/pull/18885 Refs: https://github.com/nodejs/node/pull/15324 Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- doc/api/http.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index ae789a2886dfe9..e536431759c22e 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -748,10 +748,11 @@ changes: If a client connection emits an `'error'` event, it will be forwarded here. Listener of this event is responsible for closing/destroying the underlying -socket. For example, one may wish to more gracefully close the socket with an -HTTP '400 Bad Request' response instead of abruptly severing the connection. +socket. For example, one may wish to more gracefully close the socket with a +custom HTTP response instead of abruptly severing the connection. -Default behavior is to destroy the socket immediately on malformed request. +Default behavior is to close the socket with an HTTP '400 Bad Request' response +if possible, otherwise the socket is immediately destroyed. `socket` is the [`net.Socket`][] object that the error originated from. From d2400097c1f1166f0ec1bd8099521a07571ad60e Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 20 Feb 2018 15:10:10 -0800 Subject: [PATCH 16/47] doc: remove extraneous "for example" text No need to announce obvious example code as being example code. Remove unneeded "for example" text as one small way to try to keep the docs more concise.. PR-URL: https://github.com/nodejs/node/pull/18890 Reviewed-By: Luigi Pinca Reviewed-By: Matheus Marchini Reviewed-By: Ruben Bridgewater Reviewed-By: Vse Mozhet Byt --- doc/api/addons.md | 2 +- doc/api/async_hooks.md | 8 +------- doc/api/buffer.md | 4 ---- doc/api/child_process.md | 2 -- doc/api/dns.md | 6 ------ doc/api/domain.md | 2 -- doc/api/errors.md | 11 ++--------- doc/api/fs.md | 10 ---------- doc/api/http2.md | 4 ---- doc/api/modules.md | 2 +- doc/api/n-api.md | 4 ++-- doc/api/os.md | 2 -- doc/api/path.md | 10 ---------- doc/api/process.md | 20 -------------------- doc/api/querystring.md | 2 -- doc/api/readline.md | 14 -------------- doc/api/repl.md | 2 +- doc/api/stream.md | 12 ------------ doc/api/util.md | 10 ++-------- doc/api/v8.md | 4 ---- doc/guides/using-internal-errors.md | 2 -- doc/guides/writing-tests.md | 6 ++---- 22 files changed, 12 insertions(+), 127 deletions(-) diff --git a/doc/api/addons.md b/doc/api/addons.md index c6802530f6dc67..3641a2d6ba224a 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -292,7 +292,7 @@ Each of these examples using the following `binding.gyp` file: ``` In cases where there is more than one `.cc` file, simply add the additional -filename to the `sources` array. For example: +filename to the `sources` array: ```json "sources": ["addon.cc", "myexample.cc"] diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 1cd962246b988f..c732046f8242b5 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -451,8 +451,6 @@ Note that `resolve()` does not do any observable synchronous work. rejected at this point, if the `Promise` was resolved by assuming the state of another `Promise`. -For example: - ```js new Promise((resolve) => resolve(true)).then((a) => {}); ``` @@ -481,8 +479,6 @@ changes: * Returns: {number} The `asyncId` of the current execution context. Useful to track when something calls. -For example: - ```js const async_hooks = require('async_hooks'); @@ -493,7 +489,7 @@ fs.open(path, 'r', (err, fd) => { ``` The ID returned from `executionAsyncId()` is related to execution timing, not -causality (which is covered by `triggerAsyncId()`). For example: +causality (which is covered by `triggerAsyncId()`): ```js const server = net.createServer(function onConnection(conn) { @@ -517,8 +513,6 @@ See the section on [promise execution tracking][]. * Returns: {number} The ID of the resource responsible for calling the callback that is currently being executed. -For example: - ```js const server = net.createServer((conn) => { // The resource that caused (or triggered) this callback to be called diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 439e9917803814..2b0ce95afa2f76 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -930,8 +930,6 @@ added: v8.2.0 For objects whose `valueOf()` function returns a value not strictly equal to `object`, returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)`. -For example: - ```js const buf = Buffer.from(new String('this is a test')); // @@ -940,8 +938,6 @@ const buf = Buffer.from(new String('this is a test')); For objects that support `Symbol.toPrimitive`, returns `Buffer.from(object[Symbol.toPrimitive](), offsetOrEncoding, length)`. -For example: - ```js class Foo { [Symbol.toPrimitive]() { diff --git a/doc/api/child_process.md b/doc/api/child_process.md index b3a2864c67a9f8..8c305cf9ca430c 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -226,8 +226,6 @@ a Promise for an object with `stdout` and `stderr` properties. In case of an error, a rejected promise is returned, with the same `error` object given in the callback, but with an additional two properties `stdout` and `stderr`. -For example: - ```js const util = require('util'); const exec = util.promisify(require('child_process').exec); diff --git a/doc/api/dns.md b/doc/api/dns.md index c1ec1cfa51c41c..cda4823e3ce0e7 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -114,8 +114,6 @@ Returns an array of IP address strings, formatted according to [rfc5952][], that are currently configured for DNS resolution. A string will include a port section if a custom port is used. -For example: - ```js [ @@ -369,8 +367,6 @@ function will contain an array of objects with the following properties: * `order` * `preference` -For example: - ```js { @@ -558,8 +554,6 @@ Sets the IP address and port of servers to be used when performing DNS resolution. The `servers` argument is an array of [rfc5952][] formatted addresses. If the port is the IANA default DNS port (53) it can be omitted. -For example: - ```js dns.setServers([ '4.4.4.4', diff --git a/doc/api/domain.md b/doc/api/domain.md index 77eff3a194238b..b303b0fdeb1746 100644 --- a/doc/api/domain.md +++ b/doc/api/domain.md @@ -239,8 +239,6 @@ perhaps we would like to have a separate domain to use for each request. That is possible via explicit binding. -For example: - ```js // create a top-level domain for the server const domain = require('domain'); diff --git a/doc/api/errors.md b/doc/api/errors.md index 25f3a55194dff4..95700d0f6707bf 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -292,8 +292,6 @@ console.error(err.message); The `error.stack` property is a string describing the point in the code at which the `Error` was instantiated. -For example: - ```txt Error: Things keep happening! at /home/gbusey/file.js:525:2 @@ -368,8 +366,6 @@ detailed [here](#errors_system_errors). A subclass of `Error` that indicates the failure of an assertion. Such errors commonly indicate inequality of actual and expected value. -For example: - ```js assert.strictEqual(1, 2); // AssertionError [ERR_ASSERTION]: 1 === 2 @@ -381,8 +377,6 @@ A subclass of `Error` that indicates that a provided argument was not within the set or range of acceptable values for a function; whether that is a numeric range, or outside the set of options for a given function parameter. -For example: - ```js require('net').connect(-1); // throws "RangeError: "port" option should be >= 0 and < 65536: -1" @@ -1298,9 +1292,8 @@ compiled with ICU support. ### ERR_NO_LONGER_SUPPORTED -A Node.js API was called in an unsupported manner. - -For example: `Buffer.write(string, encoding, offset[, length])` +A Node.js API was called in an unsupported manner, such as +`Buffer.write(string, encoding, offset[, length])`. ### ERR_OUT_OF_RANGE diff --git a/doc/api/fs.md b/doc/api/fs.md index 1d5679d5a4c374..dccc0d06601817 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -22,8 +22,6 @@ The arguments passed to the completion callback depend on the method, but the first argument is always reserved for an exception. If the operation was completed successfully, then the first argument will be `null` or `undefined`. -For example: - ```js const fs = require('fs'); @@ -36,8 +34,6 @@ fs.unlink('/tmp/hello', (err) => { Exceptions that occur using synchronous operations are thrown immediately and may be handled using `try`/`catch`, or may be allowed to bubble up. -For example: - ```js const fs = require('fs'); @@ -403,7 +399,6 @@ A `fs.Stats` object provides information about a file. Objects returned from [`fs.stat()`][], [`fs.lstat()`][] and [`fs.fstat()`][] and their synchronous counterparts are of this type. -For example: ```console Stats { dev: 2114, @@ -703,9 +698,6 @@ so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file is not accessible. -For example: - - **write (NOT RECOMMENDED)** ```js @@ -1354,8 +1346,6 @@ so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file does not exist. -For example: - **write (NOT RECOMMENDED)** ```js diff --git a/doc/api/http2.md b/doc/api/http2.md index 1f4eaa6fe55085..2a6e63c64e0da4 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1094,8 +1094,6 @@ received for this stream from the connected HTTP/2 server. The listener is invoked with two arguments: an Object containing the received [HTTP2 Headers Object][], and flags associated with the headers. -For example: - ```js const http2 = require('http2'); const client = http2.connect('https://localhost'); @@ -2008,8 +2006,6 @@ keys will be serialized to lower-case. Property values should be strings (if they are not they will be coerced to strings) or an Array of strings (in order to send more than one value per header field). -For example: - ```js const headers = { ':status': '200', diff --git a/doc/api/modules.md b/doc/api/modules.md index 157ec3b6f715b9..904150ccafe4fd 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -729,7 +729,7 @@ exports = { hello: false }; // Not exported, only available in the module ``` When the `module.exports` property is being completely replaced by a new -object, it is common to also reassign `exports`, for example: +object, it is common to also reassign `exports`: ```js diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 59228592e1d19f..ed22d380dc425b 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -59,8 +59,8 @@ example is: [node-api](https://github.com/nodejs/node-api). In order to use the N-API functions, include the file [node_api.h](https://github.com/nodejs/node/blob/master/src/node_api.h) -which is located in the src directory in the node development tree. -For example: +which is located in the src directory in the node development tree: + ```C #include ``` diff --git a/doc/api/os.md b/doc/api/os.md index bb25c3f6cc2b1b..69756fd65b1e8b 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -70,8 +70,6 @@ The properties included on each object include: * `idle` {number} The number of milliseconds the CPU has spent in idle mode. * `irq` {number} The number of milliseconds the CPU has spent in irq mode. -For example: - ```js [ diff --git a/doc/api/path.md b/doc/api/path.md index eb2621bfa1b208..b3569c5ea82ace 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -79,8 +79,6 @@ The `path.basename()` methods returns the last portion of a `path`, similar to the Unix `basename` command. Trailing directory separators are ignored, see [`path.sep`][]. -For example: - ```js path.basename('/foo/bar/baz/asdf/quux.html'); // Returns: 'quux.html' @@ -140,8 +138,6 @@ The `path.dirname()` method returns the directory name of a `path`, similar to the Unix `dirname` command. Trailing directory separators are ignored, see [`path.sep`][]. -For example: - ```js path.dirname('/foo/bar/baz/asdf/quux'); // Returns: '/foo/bar/baz/asdf' @@ -167,8 +163,6 @@ the `path`. If there is no `.` in the last portion of the `path`, or if the first character of the basename of `path` (see `path.basename()`) is `.`, then an empty string is returned. -For example: - ```js path.extname('index.html'); // Returns: '.html' @@ -302,8 +296,6 @@ Zero-length `path` segments are ignored. If the joined path string is a zero-length string then `'.'` will be returned, representing the current working directory. -For example: - ```js path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // Returns: '/foo/bar/baz/asdf' @@ -497,8 +489,6 @@ Zero-length `path` segments are ignored. If no `path` segments are passed, `path.resolve()` will return the absolute path of the current working directory. -For example: - ```js path.resolve('/foo/bar', './baz'); // Returns: '/foo/bar/baz' diff --git a/doc/api/process.md b/doc/api/process.md index fc2a450f9814a5..8e0da34110b437 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -58,8 +58,6 @@ The listener callback function is invoked with the exit code specified either by the [`process.exitCode`][] property, or the `exitCode` argument passed to the [`process.exit()`] method, as the only argument. -For example: - ```js process.on('exit', (code) => { console.log(`About to exit with code: ${code}`); @@ -129,8 +127,6 @@ In asynchronous code, the `'unhandledRejection'` event is emitted when the list of unhandled rejections grows, and the `'rejectionHandled'` event is emitted when the list of unhandled rejections shrinks. -For example: - ```js const unhandledRejections = new Map(); process.on('unhandledRejection', (reason, p) => { @@ -161,8 +157,6 @@ behavior. The listener function is called with the `Error` object passed as the only argument. -For example: - ```js process.on('uncaughtException', (err) => { fs.writeSync(1, `Caught exception: ${err}\n`); @@ -230,8 +224,6 @@ The listener function is called with the following arguments: (typically an [`Error`][] object). * `p` the `Promise` that was rejected. -For example: - ```js process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at:', p, 'reason:', reason); @@ -355,8 +347,6 @@ The signal handler will receive the signal's name (`'SIGINT'`, The name of each event will be the uppercase common name for the signal (e.g. `'SIGINT'` for `SIGINT` signals). -For example: - ```js // Begin reading from stdin so the process does not exit. process.stdin.resume(); @@ -925,8 +915,6 @@ include the Node.js executable, the name of the script, or any options following the script name. These options are useful in order to spawn child processes with the same execution environment as the parent. -For example: - ```console $ node --harmony script.js --version ``` @@ -955,8 +943,6 @@ added: v0.1.100 The `process.execPath` property returns the absolute pathname of the executable that started the Node.js process. -For example: - ```js '/usr/local/bin/node' @@ -1229,8 +1215,6 @@ group. really just a signal sender, like the `kill` system call. The signal sent may do something other than kill the target process. -For example: - ```js process.on('SIGHUP', () => { console.log('Got SIGHUP signal.'); @@ -1506,8 +1490,6 @@ tarball. - `'Boron'` for the 6.x LTS line beginning with 6.9.0. - `'Carbon'` for the 8.x LTS line beginning with 8.9.1. -For example: - ```js { @@ -1712,8 +1694,6 @@ The `process.stdin` property returns a stream connected to stream) unless fd `0` refers to a file, in which case it is a [Readable][] stream. -For example: - ```js process.stdin.setEncoding('utf8'); diff --git a/doc/api/querystring.md b/doc/api/querystring.md index 5bd4f1cce192a7..13593df468f4aa 100644 --- a/doc/api/querystring.md +++ b/doc/api/querystring.md @@ -108,8 +108,6 @@ It serializes the following types of values passed in `obj`: {string|number|boolean|string[]|number[]|boolean[]} Any other input values will be coerced to empty strings. -For example: - ```js querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }); // returns 'foo=bar&baz=qux&baz=quux&corge=' diff --git a/doc/api/readline.md b/doc/api/readline.md index 42d07da2d0e418..820507213291f3 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -75,8 +75,6 @@ presses the ``, or `` keys. The listener function is called with a string containing the single line of received input. -For example: - ```js rl.on('line', (input) => { console.log(`Received: ${input}`); @@ -96,8 +94,6 @@ The `'pause'` event is emitted when one of the following occur: The listener function is called without passing any arguments. -For example: - ```js rl.on('pause', () => { console.log('Readline paused.'); @@ -133,8 +129,6 @@ not be emitted. The listener function is invoked without passing any arguments. -For example: - ```js rl.on('SIGCONT', () => { // `prompt` will automatically resume the stream @@ -156,8 +150,6 @@ event will be emitted. The listener function is invoked without passing any arguments. -For example: - ```js rl.on('SIGINT', () => { rl.question('Are you sure you want to exit? ', (answer) => { @@ -184,8 +176,6 @@ paused before the process was sent to the background. The listener function is invoked without passing any arguments. -For example: - ```js rl.on('SIGTSTP', () => { // This will override SIGTSTP and prevent the program from going to the @@ -307,8 +297,6 @@ paused. If the `readline.Interface` was created with `output` set to `null` or `undefined` the `data` and `key` are not written. -For example: - ```js rl.write('Delete this!'); // Simulate Ctrl+u to delete the line written previously @@ -387,8 +375,6 @@ changes: The `readline.createInterface()` method creates a new `readline.Interface` instance. -For example: - ```js const readline = require('readline'); const rl = readline.createInterface({ diff --git a/doc/api/repl.md b/doc/api/repl.md index 506f54a4b8a2a8..076f66171213b0 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -96,7 +96,7 @@ are declared at the global scope. The default evaluator provides access to any variables that exist in the global scope. It is possible to expose a variable to the REPL explicitly by assigning -it to the `context` object associated with each `REPLServer`. For example: +it to the `context` object associated with each `REPLServer`: ```js const repl = require('repl'); diff --git a/doc/api/stream.md b/doc/api/stream.md index 529a6681e316b1..ae0093dd58ece5 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1136,8 +1136,6 @@ It will rarely be necessary to use `readable.wrap()` but the method has been provided as a convenience for interacting with older Node.js applications and libraries. -For example: - ```js const { OldReader } = require('./old-api-module.js'); const { Readable } = require('stream'); @@ -1313,8 +1311,6 @@ inheritance. This can be accomplished by directly creating instances of the `stream.Writable`, `stream.Readable`, `stream.Duplex` or `stream.Transform` objects and passing appropriate methods as constructor options. -For example: - ```js const { Writable } = require('stream'); @@ -1356,8 +1352,6 @@ constructor and implement the `writable._write()` method. The * `final` {Function} Implementation for the [`stream._final()`][stream-_final] method. -For example: - ```js const { Writable } = require('stream'); @@ -1606,8 +1600,6 @@ constructor and implement the `readable._read()` method. * `destroy` {Function} Implementation for the [`stream._destroy()`][readable-_destroy] method. -For example: - ```js const { Readable } = require('stream'); @@ -1854,8 +1846,6 @@ changes: * `writableHighWaterMark` {number} Sets `highWaterMark` for the writable side of the stream. Has no effect if `highWaterMark` is provided. -For example: - ```js const { Duplex } = require('stream'); @@ -2010,8 +2000,6 @@ the output on the Readable side is not consumed. * `flush` {Function} Implementation for the [`stream._flush()`][stream-_flush] method. -For example: - ```js const { Transform } = require('stream'); diff --git a/doc/api/util.md b/doc/api/util.md index 00d04df8f850ee..db56da1618e6f7 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -26,8 +26,6 @@ a `(err, value) => ...` callback as the last argument. In the callback, the first argument will be the rejection reason (or `null` if the Promise resolved), and the second argument will be the resolved value. -For example: - ```js const util = require('util'); @@ -86,8 +84,6 @@ environment variable. If the `section` name appears within the value of that environment variable, then the returned function operates similar to [`console.error()`][]. If not, then the returned function is a no-op. -For example: - ```js const util = require('util'); const debuglog = util.debuglog('foo'); @@ -105,7 +101,7 @@ FOO 3245: hello from foo [123] where `3245` is the process id. If it is not run with that environment variable set, then it will not print anything. -The `section` supports wildcard also, for example: +The `section` supports wildcard also: ```js const util = require('util'); const debuglog = util.debuglog('foo-bar'); @@ -119,7 +115,7 @@ FOO-BAR 3257: hi there, it's foo-bar [2333] ``` Multiple comma-separated `section` names may be specified in the `NODE_DEBUG` -environment variable. For example: `NODE_DEBUG=fs,net,tls`. +environment variable: `NODE_DEBUG=fs,net,tls`. ## util.deprecate(function, string) ```js { diff --git a/doc/guides/using-internal-errors.md b/doc/guides/using-internal-errors.md index 90962757bb0bb2..c03f44623a0f7f 100644 --- a/doc/guides/using-internal-errors.md +++ b/doc/guides/using-internal-errors.md @@ -99,8 +99,6 @@ special cases, they should only validate that the expected code is received and NOT validate the message. This will reduce the amount of test change required when the message for an error changes. -For example: - ```js assert.throws(() => { socket.bind(); diff --git a/doc/guides/writing-tests.md b/doc/guides/writing-tests.md index 949aea402db5c8..aa4412e1613c2c 100644 --- a/doc/guides/writing-tests.md +++ b/doc/guides/writing-tests.md @@ -127,7 +127,7 @@ explanation go [here](https://github.com/nodejs/testing/issues/27). In the event a test needs a timer, consider using the `common.platformTimeout()` method. It allows setting specific timeouts -depending on the platform. For example: +depending on the platform: ```javascript const timer = setTimeout(fail, common.platformTimeout(4000)); @@ -259,9 +259,7 @@ features in JavaScript code in the `lib` directory. However, when writing tests, for the ease of backporting, it is encouraged to use those ES.Next features that can be used directly without a flag in [all maintained branches][]. [node.green][] lists available features -in each release. - -For example: +in each release, such as: - `let` and `const` over `var` - Template literals over string concatenation From 5477060491c89df636230f838b7d1f3fbbc1e460 Mon Sep 17 00:00:00 2001 From: Weijia Wang <381152119@qq.com> Date: Wed, 7 Feb 2018 11:22:51 +0800 Subject: [PATCH 17/47] url: reduce deplicated codes in `autoEscapeStr` PR-URL: https://github.com/nodejs/node/pull/18613 Reviewed-By: Ruben Bridgewater --- benchmark/url/url-parse.js | 22 ++++++++ lib/url.js | 112 +++++++++---------------------------- 2 files changed, 47 insertions(+), 87 deletions(-) create mode 100644 benchmark/url/url-parse.js diff --git a/benchmark/url/url-parse.js b/benchmark/url/url-parse.js new file mode 100644 index 00000000000000..83f626ccdadfe3 --- /dev/null +++ b/benchmark/url/url-parse.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common.js'); +const url = require('url'); + +const inputs = { + normal: 'http://foo.com/bar', + escaped: 'https://foo.bar/{}^`/abcd' +}; + +const bench = common.createBenchmark(main, { + type: Object.keys(inputs), + n: [1e7] +}); + +function main({ type, n }) { + const input = inputs[type] || ''; + + bench.start(); + for (var i = 0; i < n; i += 1) + url.parse(input); + bench.end(n); +} diff --git a/lib/url.js b/lib/url.js index cb524fd9a87347..ab4b2b4647edd2 100644 --- a/lib/url.js +++ b/lib/url.js @@ -439,6 +439,24 @@ function validateHostname(self, rest, hostname) { } } +// Escaped characters. Use empty strings to fill up unused entries. +// Using Array is faster than Object/Map +const escapedCodes = [ + /*0 - 9*/ '', '', '', '', '', '', '', '', '', '%09', + /*10 - 19*/ '%0A', '', '', '%0D', '', '', '', '', '', '', + /*20 - 29*/ '', '', '', '', '', '', '', '', '', '', + /*30 - 39*/ '', '', '%20', '', '%22', '', '', '', '', '%27', + /*40 - 49*/ '', '', '', '', '', '', '', '', '', '', + /*50 - 59*/ '', '', '', '', '', '', '', '', '', '', + /*60 - 69*/ '%3C', '', '%3E', '', '', '', '', '', '', '', + /*70 - 79*/ '', '', '', '', '', '', '', '', '', '', + /*80 - 89*/ '', '', '', '', '', '', '', '', '', '', + /*90 - 99*/ '', '', '%5C', '', '%5E', '', '%60', '', '', '', + /*100 - 109*/ '', '', '', '', '', '', '', '', '', '', + /*110 - 119*/ '', '', '', '', '', '', '', '', '', '', + /*120 - 125*/ '', '', '', '%7B', '%7C', '%7D' +]; + // Automatically escape all delimiters and unwise characters from RFC 2396. // Also escape single quotes in case of an XSS attack. // Return the escaped string. @@ -446,94 +464,14 @@ function autoEscapeStr(rest) { var escaped = ''; var lastEscapedPos = 0; for (var i = 0; i < rest.length; ++i) { - // Manual switching is faster than using a Map/Object. // `escaped` contains substring up to the last escaped character. - switch (rest.charCodeAt(i)) { - case 9: // '\t' - // Concat if there are ordinary characters in the middle. - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%09'; - lastEscapedPos = i + 1; - break; - case 10: // '\n' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%0A'; - lastEscapedPos = i + 1; - break; - case 13: // '\r' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%0D'; - lastEscapedPos = i + 1; - break; - case 32: // ' ' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%20'; - lastEscapedPos = i + 1; - break; - case 34: // '"' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%22'; - lastEscapedPos = i + 1; - break; - case 39: // '\'' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%27'; - lastEscapedPos = i + 1; - break; - case 60: // '<' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%3C'; - lastEscapedPos = i + 1; - break; - case 62: // '>' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%3E'; - lastEscapedPos = i + 1; - break; - case 92: // '\\' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%5C'; - lastEscapedPos = i + 1; - break; - case 94: // '^' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%5E'; - lastEscapedPos = i + 1; - break; - case 96: // '`' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%60'; - lastEscapedPos = i + 1; - break; - case 123: // '{' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%7B'; - lastEscapedPos = i + 1; - break; - case 124: // '|' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%7C'; - lastEscapedPos = i + 1; - break; - case 125: // '}' - if (i > lastEscapedPos) - escaped += rest.slice(lastEscapedPos, i); - escaped += '%7D'; - lastEscapedPos = i + 1; - break; + var escapedChar = escapedCodes[rest.charCodeAt(i)]; + if (escapedChar) { + // Concat if there are ordinary characters in the middle. + if (i > lastEscapedPos) + escaped += rest.slice(lastEscapedPos, i); + escaped += escapedChar; + lastEscapedPos = i + 1; } } if (lastEscapedPos === 0) // Nothing has been escaped. From d58dcec33d8475f1d45028a1e7a1bd363a19e694 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Wed, 21 Feb 2018 15:42:26 -0500 Subject: [PATCH 18/47] deps: upgrade libuv to 1.19.2 PR-URL: https://github.com/nodejs/node/pull/18918 Reviewed-By: Ben Noordhuis Reviewed-By: Joyee Cheung --- deps/uv/AUTHORS | 5 + deps/uv/ChangeLog | 45 ++++ deps/uv/SUPPORTED_PLATFORMS.md | 1 + deps/uv/common.gypi | 8 +- deps/uv/configure.ac | 2 +- deps/uv/docs/src/handle.rst | 3 + deps/uv/docs/src/loop.rst | 2 +- deps/uv/docs/src/threading.rst | 11 +- deps/uv/docs/src/timer.rst | 2 + deps/uv/gyp_uv.py | 23 +- deps/uv/include/uv-errno.h | 132 +++++----- deps/uv/include/uv-version.h | 2 +- deps/uv/include/uv.h | 1 + deps/uv/libuv.pc.in | 1 + deps/uv/src/unix/aix-common.c | 26 +- deps/uv/src/unix/aix.c | 46 ++-- deps/uv/src/unix/async.c | 8 +- deps/uv/src/unix/bsd-ifaddrs.c | 4 +- deps/uv/src/unix/core.c | 102 ++++---- deps/uv/src/unix/cygwin.c | 2 +- deps/uv/src/unix/darwin-proctitle.c | 13 +- deps/uv/src/unix/darwin.c | 24 +- deps/uv/src/unix/freebsd.c | 34 +-- deps/uv/src/unix/fs.c | 61 ++--- deps/uv/src/unix/fsevents.c | 26 +- deps/uv/src/unix/getaddrinfo.c | 10 +- deps/uv/src/unix/getnameinfo.c | 2 +- deps/uv/src/unix/ibmi.c | 2 +- deps/uv/src/unix/internal.h | 1 + deps/uv/src/unix/kqueue.c | 8 +- deps/uv/src/unix/linux-core.c | 34 +-- deps/uv/src/unix/linux-inotify.c | 10 +- deps/uv/src/unix/loop-watcher.c | 2 +- deps/uv/src/unix/netbsd.c | 28 +-- deps/uv/src/unix/no-fsevents.c | 6 +- deps/uv/src/unix/no-proctitle.c | 2 +- deps/uv/src/unix/openbsd.c | 32 +-- deps/uv/src/unix/os390.c | 38 +-- deps/uv/src/unix/pipe.c | 30 +-- deps/uv/src/unix/poll.c | 4 +- deps/uv/src/unix/posix-poll.c | 4 +- deps/uv/src/unix/process.c | 40 ++-- deps/uv/src/unix/procfs-exepath.c | 4 +- deps/uv/src/unix/proctitle.c | 4 +- deps/uv/src/unix/signal.c | 4 +- deps/uv/src/unix/stream.c | 84 ++++--- deps/uv/src/unix/sunos.c | 42 ++-- deps/uv/src/unix/tcp.c | 46 ++-- deps/uv/src/unix/thread.c | 61 +++-- deps/uv/src/unix/timer.c | 4 +- deps/uv/src/unix/tty.c | 16 +- deps/uv/src/unix/udp.c | 76 +++--- deps/uv/src/uv-common.h | 6 + deps/uv/src/win/fs.c | 1 + deps/uv/src/win/process.c | 11 +- deps/uv/src/win/winsock.c | 6 +- deps/uv/test/test-condvar.c | 54 ++++- deps/uv/test/test-fork.c | 2 +- deps/uv/test/test-fs-copyfile.c | 11 +- deps/uv/test/test-fs-event.c | 4 +- deps/uv/test/test-fs.c | 42 +++- deps/uv/test/test-ipc-send-recv.c | 2 +- deps/uv/test/test-list.h | 2 + deps/uv/test/test.gyp | 279 ++++++++++++++++++++++ deps/uv/uv.gyp | 357 ++++------------------------ deps/uv/vcbuild.bat | 7 +- 66 files changed, 1071 insertions(+), 891 deletions(-) create mode 100644 deps/uv/test/test.gyp diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index c826c8e13306a7..fcb0aac3e83b10 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -325,3 +325,8 @@ Anna Henningsen JĆ©rĆ©my Lal Ben Wijen elephantp +Felix Yan +Mason X +Jesse Gorzinski +Ryuichi KAWAMATA +Joyee Cheung diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 163500245bf560..509a1d1571d498 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,48 @@ +2018.02.22, Version 1.19.2 (Stable), c5afc37e2a8a70d8ab0da8dac10b77ba78c0488c + +Changes since version 1.19.1: + +* test: fix incorrect asserts (cjihrig) + +* test: fix a typo in test-fork.c (Felix Yan) + +* build: remove long-obsolete gyp workarounds (Ben Noordhuis) + +* build: split off tests into separate gyp file (Ben Noordhuis) + +* test: check uv_cond_timedwait more carefully (Jamie Davis) + +* include,src: introduce UV__ERR() macro (Mason X) + +* build: add url field to libuv.pc (Ben Noordhuis) + +* doc: mark IBM i as Tier 3 support (Jesse Gorzinski) + +* win,build: correct C2059 errors (Michael Fero) + +* zos: fix timeout for condition variable (jBarz) + +* win: CREATE_NO_WINDOW when stdio is not inherited (Nick Logan) + +* build: fix commmon.gypi comment (Ryuichi KAWAMATA) + +* doc: document uv_timer_start() on an active timer (VladimĆ­r ČunĆ”t) + +* doc: add note about handle movability (Bartosz Sosnowski) + +* doc: fix syntax error in loop documentation (Bartosz Sosnowski) + +* osx,stream: retry sending handle on EMSGSIZE error (Santiago Gimeno) + +* unix: delay fs req register until after validation (cjihrig) + +* test: add tests for bad inputs (Joyee Cheung) + +* unix,win: ensure req->bufs is freed (cjihrig) + +* test: add additional fs memory management checks (cjihrig) + + 2018.01.20, Version 1.19.1 (Stable), 8202d1751196c2374ad370f7f3779daef89befae Changes since version 1.19.0: diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md index c56913bbc2fff1..077191086ce524 100644 --- a/deps/uv/SUPPORTED_PLATFORMS.md +++ b/deps/uv/SUPPORTED_PLATFORMS.md @@ -11,6 +11,7 @@ | Linux with musl | Tier 2 | musl >= 1.0 | | | SmartOS | Tier 2 | >= 14.4 | Maintainers: @libuv/smartos | | Android | Tier 3 | NDK >= r15b | | +| IBM i | Tier 3 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | MinGW | Tier 3 | MinGW32 and MinGW-w64 | | | SunOS | Tier 3 | Solaris 121 and later | | | Other | Tier 3 | N/A | | diff --git a/deps/uv/common.gypi b/deps/uv/common.gypi index 816847bfc70690..572a1633b0b555 100644 --- a/deps/uv/common.gypi +++ b/deps/uv/common.gypi @@ -16,9 +16,9 @@ 'VCCLCompilerTool': { 'target_conditions': [ ['uv_library=="static_library"', { - 'RuntimeLibrary': 1, # static debug + 'RuntimeLibrary': 1, # /MTd static debug }, { - 'RuntimeLibrary': 3, # DLL debug + 'RuntimeLibrary': 3, # /MDd DLL debug }], ], 'Optimization': 0, # /Od, no optimization @@ -52,9 +52,9 @@ 'VCCLCompilerTool': { 'target_conditions': [ ['uv_library=="static_library"', { - 'RuntimeLibrary': 0, # static release + 'RuntimeLibrary': 0, # /MT static release }, { - 'RuntimeLibrary': 2, # debug release + 'RuntimeLibrary': 2, # /MD DLL release }], ], 'Optimization': 3, # /Ox, full optimization diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 75fb13c8ce7b23..4074e77841d489 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.19.1], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.19.2], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/handle.rst b/deps/uv/docs/src/handle.rst index e4cb90b5f7e14b..cdfb76bf8c8928 100644 --- a/deps/uv/docs/src/handle.rst +++ b/deps/uv/docs/src/handle.rst @@ -9,6 +9,9 @@ Structures are aligned so that any libuv handle can be cast to `uv_handle_t`. All API functions defined here work with any handle type. +Libuv handles are not movable. Pointers to handle structures passed to +functions must remain valid for the duration of the requested operation. Take +care when using stack allocated handles. Data types ---------- diff --git a/deps/uv/docs/src/loop.rst b/deps/uv/docs/src/loop.rst index dcde5049ac2baa..86a99adf5d669c 100644 --- a/deps/uv/docs/src/loop.rst +++ b/deps/uv/docs/src/loop.rst @@ -219,7 +219,7 @@ API .. caution:: - Any previous value returned from :c:func`uv_backend_fd` is now + Any previous value returned from :c:func:`uv_backend_fd` is now invalid. That function must be called again to determine the correct backend file descriptor. diff --git a/deps/uv/docs/src/threading.rst b/deps/uv/docs/src/threading.rst index bca8ba1d0636f4..89bb4a6f3ae14e 100644 --- a/deps/uv/docs/src/threading.rst +++ b/deps/uv/docs/src/threading.rst @@ -131,8 +131,15 @@ Functions return 0 on success or an error code < 0 (unless the return type is void, of course). .. note:: - Callers should be prepared to deal with spurious wakeups on :c:func:`uv_cond_wait` and - :c:func:`uv_cond_timedwait`. + 1. Callers should be prepared to deal with spurious wakeups on :c:func:`uv_cond_wait` + and :c:func:`uv_cond_timedwait`. + 2. The timeout parameter for :c:func:`uv_cond_timedwait` is relative to the time + at which function is called. + 3. On z/OS, the timeout parameter for :c:func:`uv_cond_timedwait` is converted to an + absolute system time at which the wait expires. If the current system clock time + passes the absolute time calculated before the condition is signaled, an ETIMEDOUT + error results. After the wait begins, the wait time is not affected by changes + to the system clock. .. c:function:: int uv_cond_init(uv_cond_t* cond) .. c:function:: void uv_cond_destroy(uv_cond_t* cond) diff --git a/deps/uv/docs/src/timer.rst b/deps/uv/docs/src/timer.rst index 8e11f257f8ad16..e163e288fdb275 100644 --- a/deps/uv/docs/src/timer.rst +++ b/deps/uv/docs/src/timer.rst @@ -45,6 +45,8 @@ API .. note:: Does not update the event loop's concept of "now". See :c:func:`uv_update_time` for more information. + If the timer is already active, it is simply updated. + .. c:function:: int uv_timer_stop(uv_timer_t* handle) Stop the timer, the callback will not be called anymore. diff --git a/deps/uv/gyp_uv.py b/deps/uv/gyp_uv.py index bd37d95c4713f6..c2add5caecc7d3 100755 --- a/deps/uv/gyp_uv.py +++ b/deps/uv/gyp_uv.py @@ -43,28 +43,7 @@ def run_gyp(args): if __name__ == '__main__': args = sys.argv[1:] - - # GYP bug. - # On msvs it will crash if it gets an absolute path. - # On Mac/make it will crash if it doesn't get an absolute path. - if sys.platform == 'win32': - args.append(os.path.join(uv_root, 'uv.gyp')) - common_fn = os.path.join(uv_root, 'common.gypi') - options_fn = os.path.join(uv_root, 'options.gypi') - # we force vs 2010 over 2008 which would otherwise be the default for gyp - if not os.environ.get('GYP_MSVS_VERSION'): - os.environ['GYP_MSVS_VERSION'] = '2010' - else: - args.append(os.path.join(os.path.abspath(uv_root), 'uv.gyp')) - common_fn = os.path.join(os.path.abspath(uv_root), 'common.gypi') - options_fn = os.path.join(os.path.abspath(uv_root), 'options.gypi') - - if os.path.exists(common_fn): - args.extend(['-I', common_fn]) - - if os.path.exists(options_fn): - args.extend(['-I', options_fn]) - + args.extend('-I common.gypi test/test.gyp'.split(' ')) args.append('--depth=' + uv_root) # There's a bug with windows which doesn't allow this feature. diff --git a/deps/uv/include/uv-errno.h b/deps/uv/include/uv-errno.h index 8a415331479873..aa4d4509f60dd1 100644 --- a/deps/uv/include/uv-errno.h +++ b/deps/uv/include/uv-errno.h @@ -23,6 +23,11 @@ #define UV_ERRNO_H_ #include +#if EDOM > 0 +# define UV__ERR(x) (-(x)) +#else +# define UV__ERR(x) (x) +#endif #define UV__EOF (-4095) #define UV__UNKNOWN (-4094) @@ -46,355 +51,355 @@ * a fairly common practice for Windows programmers to redefine errno codes. */ #if defined(E2BIG) && !defined(_WIN32) -# define UV__E2BIG (-E2BIG) +# define UV__E2BIG UV__ERR(E2BIG) #else # define UV__E2BIG (-4093) #endif #if defined(EACCES) && !defined(_WIN32) -# define UV__EACCES (-EACCES) +# define UV__EACCES UV__ERR(EACCES) #else # define UV__EACCES (-4092) #endif #if defined(EADDRINUSE) && !defined(_WIN32) -# define UV__EADDRINUSE (-EADDRINUSE) +# define UV__EADDRINUSE UV__ERR(EADDRINUSE) #else # define UV__EADDRINUSE (-4091) #endif #if defined(EADDRNOTAVAIL) && !defined(_WIN32) -# define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL) +# define UV__EADDRNOTAVAIL UV__ERR(EADDRNOTAVAIL) #else # define UV__EADDRNOTAVAIL (-4090) #endif #if defined(EAFNOSUPPORT) && !defined(_WIN32) -# define UV__EAFNOSUPPORT (-EAFNOSUPPORT) +# define UV__EAFNOSUPPORT UV__ERR(EAFNOSUPPORT) #else # define UV__EAFNOSUPPORT (-4089) #endif #if defined(EAGAIN) && !defined(_WIN32) -# define UV__EAGAIN (-EAGAIN) +# define UV__EAGAIN UV__ERR(EAGAIN) #else # define UV__EAGAIN (-4088) #endif #if defined(EALREADY) && !defined(_WIN32) -# define UV__EALREADY (-EALREADY) +# define UV__EALREADY UV__ERR(EALREADY) #else # define UV__EALREADY (-4084) #endif #if defined(EBADF) && !defined(_WIN32) -# define UV__EBADF (-EBADF) +# define UV__EBADF UV__ERR(EBADF) #else # define UV__EBADF (-4083) #endif #if defined(EBUSY) && !defined(_WIN32) -# define UV__EBUSY (-EBUSY) +# define UV__EBUSY UV__ERR(EBUSY) #else # define UV__EBUSY (-4082) #endif #if defined(ECANCELED) && !defined(_WIN32) -# define UV__ECANCELED (-ECANCELED) +# define UV__ECANCELED UV__ERR(ECANCELED) #else # define UV__ECANCELED (-4081) #endif #if defined(ECHARSET) && !defined(_WIN32) -# define UV__ECHARSET (-ECHARSET) +# define UV__ECHARSET UV__ERR(ECHARSET) #else # define UV__ECHARSET (-4080) #endif #if defined(ECONNABORTED) && !defined(_WIN32) -# define UV__ECONNABORTED (-ECONNABORTED) +# define UV__ECONNABORTED UV__ERR(ECONNABORTED) #else # define UV__ECONNABORTED (-4079) #endif #if defined(ECONNREFUSED) && !defined(_WIN32) -# define UV__ECONNREFUSED (-ECONNREFUSED) +# define UV__ECONNREFUSED UV__ERR(ECONNREFUSED) #else # define UV__ECONNREFUSED (-4078) #endif #if defined(ECONNRESET) && !defined(_WIN32) -# define UV__ECONNRESET (-ECONNRESET) +# define UV__ECONNRESET UV__ERR(ECONNRESET) #else # define UV__ECONNRESET (-4077) #endif #if defined(EDESTADDRREQ) && !defined(_WIN32) -# define UV__EDESTADDRREQ (-EDESTADDRREQ) +# define UV__EDESTADDRREQ UV__ERR(EDESTADDRREQ) #else # define UV__EDESTADDRREQ (-4076) #endif #if defined(EEXIST) && !defined(_WIN32) -# define UV__EEXIST (-EEXIST) +# define UV__EEXIST UV__ERR(EEXIST) #else # define UV__EEXIST (-4075) #endif #if defined(EFAULT) && !defined(_WIN32) -# define UV__EFAULT (-EFAULT) +# define UV__EFAULT UV__ERR(EFAULT) #else # define UV__EFAULT (-4074) #endif #if defined(EHOSTUNREACH) && !defined(_WIN32) -# define UV__EHOSTUNREACH (-EHOSTUNREACH) +# define UV__EHOSTUNREACH UV__ERR(EHOSTUNREACH) #else # define UV__EHOSTUNREACH (-4073) #endif #if defined(EINTR) && !defined(_WIN32) -# define UV__EINTR (-EINTR) +# define UV__EINTR UV__ERR(EINTR) #else # define UV__EINTR (-4072) #endif #if defined(EINVAL) && !defined(_WIN32) -# define UV__EINVAL (-EINVAL) +# define UV__EINVAL UV__ERR(EINVAL) #else # define UV__EINVAL (-4071) #endif #if defined(EIO) && !defined(_WIN32) -# define UV__EIO (-EIO) +# define UV__EIO UV__ERR(EIO) #else # define UV__EIO (-4070) #endif #if defined(EISCONN) && !defined(_WIN32) -# define UV__EISCONN (-EISCONN) +# define UV__EISCONN UV__ERR(EISCONN) #else # define UV__EISCONN (-4069) #endif #if defined(EISDIR) && !defined(_WIN32) -# define UV__EISDIR (-EISDIR) +# define UV__EISDIR UV__ERR(EISDIR) #else # define UV__EISDIR (-4068) #endif #if defined(ELOOP) && !defined(_WIN32) -# define UV__ELOOP (-ELOOP) +# define UV__ELOOP UV__ERR(ELOOP) #else # define UV__ELOOP (-4067) #endif #if defined(EMFILE) && !defined(_WIN32) -# define UV__EMFILE (-EMFILE) +# define UV__EMFILE UV__ERR(EMFILE) #else # define UV__EMFILE (-4066) #endif #if defined(EMSGSIZE) && !defined(_WIN32) -# define UV__EMSGSIZE (-EMSGSIZE) +# define UV__EMSGSIZE UV__ERR(EMSGSIZE) #else # define UV__EMSGSIZE (-4065) #endif #if defined(ENAMETOOLONG) && !defined(_WIN32) -# define UV__ENAMETOOLONG (-ENAMETOOLONG) +# define UV__ENAMETOOLONG UV__ERR(ENAMETOOLONG) #else # define UV__ENAMETOOLONG (-4064) #endif #if defined(ENETDOWN) && !defined(_WIN32) -# define UV__ENETDOWN (-ENETDOWN) +# define UV__ENETDOWN UV__ERR(ENETDOWN) #else # define UV__ENETDOWN (-4063) #endif #if defined(ENETUNREACH) && !defined(_WIN32) -# define UV__ENETUNREACH (-ENETUNREACH) +# define UV__ENETUNREACH UV__ERR(ENETUNREACH) #else # define UV__ENETUNREACH (-4062) #endif #if defined(ENFILE) && !defined(_WIN32) -# define UV__ENFILE (-ENFILE) +# define UV__ENFILE UV__ERR(ENFILE) #else # define UV__ENFILE (-4061) #endif #if defined(ENOBUFS) && !defined(_WIN32) -# define UV__ENOBUFS (-ENOBUFS) +# define UV__ENOBUFS UV__ERR(ENOBUFS) #else # define UV__ENOBUFS (-4060) #endif #if defined(ENODEV) && !defined(_WIN32) -# define UV__ENODEV (-ENODEV) +# define UV__ENODEV UV__ERR(ENODEV) #else # define UV__ENODEV (-4059) #endif #if defined(ENOENT) && !defined(_WIN32) -# define UV__ENOENT (-ENOENT) +# define UV__ENOENT UV__ERR(ENOENT) #else # define UV__ENOENT (-4058) #endif #if defined(ENOMEM) && !defined(_WIN32) -# define UV__ENOMEM (-ENOMEM) +# define UV__ENOMEM UV__ERR(ENOMEM) #else # define UV__ENOMEM (-4057) #endif #if defined(ENONET) && !defined(_WIN32) -# define UV__ENONET (-ENONET) +# define UV__ENONET UV__ERR(ENONET) #else # define UV__ENONET (-4056) #endif #if defined(ENOSPC) && !defined(_WIN32) -# define UV__ENOSPC (-ENOSPC) +# define UV__ENOSPC UV__ERR(ENOSPC) #else # define UV__ENOSPC (-4055) #endif #if defined(ENOSYS) && !defined(_WIN32) -# define UV__ENOSYS (-ENOSYS) +# define UV__ENOSYS UV__ERR(ENOSYS) #else # define UV__ENOSYS (-4054) #endif #if defined(ENOTCONN) && !defined(_WIN32) -# define UV__ENOTCONN (-ENOTCONN) +# define UV__ENOTCONN UV__ERR(ENOTCONN) #else # define UV__ENOTCONN (-4053) #endif #if defined(ENOTDIR) && !defined(_WIN32) -# define UV__ENOTDIR (-ENOTDIR) +# define UV__ENOTDIR UV__ERR(ENOTDIR) #else # define UV__ENOTDIR (-4052) #endif #if defined(ENOTEMPTY) && !defined(_WIN32) -# define UV__ENOTEMPTY (-ENOTEMPTY) +# define UV__ENOTEMPTY UV__ERR(ENOTEMPTY) #else # define UV__ENOTEMPTY (-4051) #endif #if defined(ENOTSOCK) && !defined(_WIN32) -# define UV__ENOTSOCK (-ENOTSOCK) +# define UV__ENOTSOCK UV__ERR(ENOTSOCK) #else # define UV__ENOTSOCK (-4050) #endif #if defined(ENOTSUP) && !defined(_WIN32) -# define UV__ENOTSUP (-ENOTSUP) +# define UV__ENOTSUP UV__ERR(ENOTSUP) #else # define UV__ENOTSUP (-4049) #endif #if defined(EPERM) && !defined(_WIN32) -# define UV__EPERM (-EPERM) +# define UV__EPERM UV__ERR(EPERM) #else # define UV__EPERM (-4048) #endif #if defined(EPIPE) && !defined(_WIN32) -# define UV__EPIPE (-EPIPE) +# define UV__EPIPE UV__ERR(EPIPE) #else # define UV__EPIPE (-4047) #endif #if defined(EPROTO) && !defined(_WIN32) -# define UV__EPROTO (-EPROTO) +# define UV__EPROTO UV__ERR(EPROTO) #else -# define UV__EPROTO (-4046) +# define UV__EPROTO UV__ERR(4046) #endif #if defined(EPROTONOSUPPORT) && !defined(_WIN32) -# define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT) +# define UV__EPROTONOSUPPORT UV__ERR(EPROTONOSUPPORT) #else # define UV__EPROTONOSUPPORT (-4045) #endif #if defined(EPROTOTYPE) && !defined(_WIN32) -# define UV__EPROTOTYPE (-EPROTOTYPE) +# define UV__EPROTOTYPE UV__ERR(EPROTOTYPE) #else # define UV__EPROTOTYPE (-4044) #endif #if defined(EROFS) && !defined(_WIN32) -# define UV__EROFS (-EROFS) +# define UV__EROFS UV__ERR(EROFS) #else # define UV__EROFS (-4043) #endif #if defined(ESHUTDOWN) && !defined(_WIN32) -# define UV__ESHUTDOWN (-ESHUTDOWN) +# define UV__ESHUTDOWN UV__ERR(ESHUTDOWN) #else # define UV__ESHUTDOWN (-4042) #endif #if defined(ESPIPE) && !defined(_WIN32) -# define UV__ESPIPE (-ESPIPE) +# define UV__ESPIPE UV__ERR(ESPIPE) #else # define UV__ESPIPE (-4041) #endif #if defined(ESRCH) && !defined(_WIN32) -# define UV__ESRCH (-ESRCH) +# define UV__ESRCH UV__ERR(ESRCH) #else # define UV__ESRCH (-4040) #endif #if defined(ETIMEDOUT) && !defined(_WIN32) -# define UV__ETIMEDOUT (-ETIMEDOUT) +# define UV__ETIMEDOUT UV__ERR(ETIMEDOUT) #else # define UV__ETIMEDOUT (-4039) #endif #if defined(ETXTBSY) && !defined(_WIN32) -# define UV__ETXTBSY (-ETXTBSY) +# define UV__ETXTBSY UV__ERR(ETXTBSY) #else # define UV__ETXTBSY (-4038) #endif #if defined(EXDEV) && !defined(_WIN32) -# define UV__EXDEV (-EXDEV) +# define UV__EXDEV UV__ERR(EXDEV) #else # define UV__EXDEV (-4037) #endif #if defined(EFBIG) && !defined(_WIN32) -# define UV__EFBIG (-EFBIG) +# define UV__EFBIG UV__ERR(EFBIG) #else # define UV__EFBIG (-4036) #endif #if defined(ENOPROTOOPT) && !defined(_WIN32) -# define UV__ENOPROTOOPT (-ENOPROTOOPT) +# define UV__ENOPROTOOPT UV__ERR(ENOPROTOOPT) #else # define UV__ENOPROTOOPT (-4035) #endif #if defined(ERANGE) && !defined(_WIN32) -# define UV__ERANGE (-ERANGE) +# define UV__ERANGE UV__ERR(ERANGE) #else # define UV__ERANGE (-4034) #endif #if defined(ENXIO) && !defined(_WIN32) -# define UV__ENXIO (-ENXIO) +# define UV__ENXIO UV__ERR(ENXIO) #else # define UV__ENXIO (-4033) #endif #if defined(EMLINK) && !defined(_WIN32) -# define UV__EMLINK (-EMLINK) +# define UV__EMLINK UV__ERR(EMLINK) #else # define UV__EMLINK (-4032) #endif @@ -404,7 +409,7 @@ * icky to hard-code it. */ #if defined(EHOSTDOWN) && !defined(_WIN32) -# define UV__EHOSTDOWN (-EHOSTDOWN) +# define UV__EHOSTDOWN UV__ERR(EHOSTDOWN) #elif defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ @@ -417,15 +422,16 @@ #endif #if defined(EREMOTEIO) && !defined(_WIN32) -# define UV__EREMOTEIO (-EREMOTEIO) +# define UV__EREMOTEIO UV__ERR(EREMOTEIO) #else # define UV__EREMOTEIO (-4030) #endif #if defined(ENOTTY) && !defined(_WIN32) -# define UV__ENOTTY (-ENOTTY) +# define UV__ENOTTY UV__ERR(ENOTTY) #else # define UV__ENOTTY (-4029) #endif + #endif /* UV_ERRNO_H_ */ diff --git a/deps/uv/include/uv-version.h b/deps/uv/include/uv-version.h index 581d761df98139..c2753d51c7c36e 100644 --- a/deps/uv/include/uv-version.h +++ b/deps/uv/include/uv-version.h @@ -32,7 +32,7 @@ #define UV_VERSION_MAJOR 1 #define UV_VERSION_MINOR 19 -#define UV_VERSION_PATCH 1 +#define UV_VERSION_PATCH 2 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 3a061132cce528..9794d9969b620e 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1559,6 +1559,7 @@ UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data); #undef UV_SIGNAL_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_PLATFORM_FIELDS +#undef UV__ERR #ifdef __cplusplus } diff --git a/deps/uv/libuv.pc.in b/deps/uv/libuv.pc.in index 9174fe151d7421..55c4b65d5dc5cf 100644 --- a/deps/uv/libuv.pc.in +++ b/deps/uv/libuv.pc.in @@ -6,6 +6,7 @@ includedir=@includedir@ Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Description: multi-platform support library with a focus on asynchronous I/O. +URL: http://libuv.org/ Libs: -L${libdir} -luv @LIBS@ Cflags: -I${includedir} diff --git a/deps/uv/src/unix/aix-common.c b/deps/uv/src/unix/aix-common.c index 2cfe8be6f11830..e17e449481836a 100644 --- a/deps/uv/src/unix/aix-common.c +++ b/deps/uv/src/unix/aix-common.c @@ -83,12 +83,12 @@ int uv_exepath(char* buffer, size_t* size) { struct procsinfo pi; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; pi.pi_pid = getpid(); res = getargs(&pi, sizeof(pi), args, sizeof(args)); if (res < 0) - return -EINVAL; + return UV_EINVAL; /* * Possibilities for args: @@ -101,7 +101,7 @@ int uv_exepath(char* buffer, size_t* size) { /* Case i) and ii) absolute or relative paths */ if (strchr(args, '/') != NULL) { if (realpath(args, abspath) != abspath) - return -errno; + return UV__ERR(errno); abspath_size = strlen(abspath); @@ -121,11 +121,11 @@ int uv_exepath(char* buffer, size_t* size) { char *path = getenv("PATH"); if (path == NULL) - return -EINVAL; + return UV_EINVAL; clonedpath = uv__strdup(path); if (clonedpath == NULL) - return -ENOMEM; + return UV_ENOMEM; token = strtok(clonedpath, ":"); while (token != NULL) { @@ -151,7 +151,7 @@ int uv_exepath(char* buffer, size_t* size) { uv__free(clonedpath); /* Out of tokens (path entries), and no match found */ - return -EINVAL; + return UV_EINVAL; } } @@ -177,19 +177,19 @@ int uv_interface_addresses(uv_interface_address_t** addresses, *count = 0; if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { - return -errno; + return UV__ERR(errno); } if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } ifc.ifc_req = (struct ifreq*)uv__malloc(size); ifc.ifc_len = size; if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } #define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) @@ -208,7 +208,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) @@ -221,7 +221,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); if (!(*addresses)) { uv__close(sockfd); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; @@ -240,7 +240,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { uv__close(sockfd); - return -ENOSYS; + return UV_ENOSYS; } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) @@ -260,7 +260,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { uv__close(sockfd); - return -ENOSYS; + return UV_ENOSYS; } if (inet6) diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index fd413090feddf0..92de8148341e10 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -119,7 +119,7 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { pc.fd = fd; if (pollset_ctl(loop->backend_fd, &pc, 1)) - return -errno; + return UV__ERR(errno); pc.cmd = PS_DELETE; if (pollset_ctl(loop->backend_fd, &pc, 1)) @@ -409,22 +409,22 @@ static int uv__is_ahafs_mounted(void){ p = uv__malloc(siz); if (p == NULL) - return -errno; + return UV__ERR(errno); /* Retrieve all mounted filesystems */ rv = mntctl(MCTL_QUERY, siz, (char*)p); if (rv < 0) - return -errno; + return UV__ERR(errno); if (rv == 0) { /* buffer was not large enough, reallocate to correct size */ siz = *(int*)p; uv__free(p); p = uv__malloc(siz); if (p == NULL) - return -errno; + return UV__ERR(errno); rv = mntctl(MCTL_QUERY, siz, (char*)p); if (rv < 0) - return -errno; + return UV__ERR(errno); } /* Look for dev in filesystems mount info */ @@ -495,7 +495,7 @@ static int uv__make_subdirs_p(const char *filename) { rc = uv__makedir_p(cmd); if (rc == -1 && errno != EEXIST){ - return -errno; + return UV__ERR(errno); } return rc; @@ -522,7 +522,7 @@ static int uv__setup_ahafs(const char* filename, int *fd) { sprintf(mon_file, "/aha/fs/modFile.monFactory"); if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX) - return -ENAMETOOLONG; + return UV_ENAMETOOLONG; /* Make the necessary subdirectories for the monitor file */ rc = uv__make_subdirs_p(filename); @@ -537,7 +537,7 @@ static int uv__setup_ahafs(const char* filename, int *fd) { /* Open the monitor file, creating it if necessary */ *fd = open(mon_file, O_CREAT|O_RDWR); if (*fd < 0) - return -errno; + return UV__ERR(errno); /* Write out the monitoring specifications. * In this case, we are monitoring for a state change event type @@ -558,7 +558,7 @@ static int uv__setup_ahafs(const char* filename, int *fd) { rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1); if (rc < 0) - return -errno; + return UV__ERR(errno); return 0; } @@ -716,7 +716,7 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; #else - return -ENOSYS; + return UV_ENOSYS; #endif } @@ -771,7 +771,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, return 0; #else - return -ENOSYS; + return UV_ENOSYS; #endif } @@ -796,7 +796,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { return 0; #else - return -ENOSYS; + return UV_ENOSYS; #endif } @@ -861,7 +861,7 @@ int uv_set_process_title(const char* title) { */ new_title = uv__strdup(title); if (new_title == NULL) - return -ENOMEM; + return UV_ENOMEM; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -888,9 +888,9 @@ int uv_get_process_title(char* buffer, size_t size) { size_t len; len = strlen(process_argv[0]); if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; else if (size <= len) - return -ENOBUFS; + return UV_ENOBUFS; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -919,10 +919,10 @@ int uv_resident_set_memory(size_t* rss) { fd = open(pp, O_RDONLY); if (fd == -1) - return -errno; + return UV__ERR(errno); /* FIXME(bnoordhuis) Handle EINTR. */ - err = -EINVAL; + err = UV_EINVAL; if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { *rss = (size_t)psinfo.pr_rssize * 1024; err = 0; @@ -953,7 +953,7 @@ int uv_uptime(double* uptime) { endutent(); if (boot_time == 0) - return -ENOSYS; + return UV_ENOSYS; *uptime = time(NULL) - boot_time; return 0; @@ -969,30 +969,30 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); if (result == -1) { - return -ENOSYS; + return UV_ENOSYS; } ncpus = result = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0); if (result == -1) { - return -ENOSYS; + return UV_ENOSYS; } ps_cpus = (perfstat_cpu_t*) uv__malloc(ncpus * sizeof(perfstat_cpu_t)); if (!ps_cpus) { - return -ENOMEM; + return UV_ENOMEM; } strcpy(cpu_id.name, FIRST_CPU); result = perfstat_cpu(&cpu_id, ps_cpus, sizeof(perfstat_cpu_t), ncpus); if (result == -1) { uv__free(ps_cpus); - return -ENOSYS; + return UV_ENOSYS; } *cpu_infos = (uv_cpu_info_t*) uv__malloc(ncpus * sizeof(uv_cpu_info_t)); if (!*cpu_infos) { uv__free(ps_cpus); - return -ENOMEM; + return UV_ENOMEM; } *count = ncpus; diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 45c088ea1b39bc..0b450ae0da7f37 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -166,7 +166,7 @@ static int uv__async_start(uv_loop_t* loop) { pipefd[0] = err; pipefd[1] = -1; } - else if (err == -ENOSYS) { + else if (err == UV_ENOSYS) { err = uv__make_pipe(pipefd, UV__F_NONBLOCK); #if defined(__linux__) /* Save a file descriptor by opening one of the pipe descriptors as @@ -240,7 +240,7 @@ static int uv__async_eventfd(void) { return fd; if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); no_eventfd2 = 1; @@ -257,7 +257,7 @@ static int uv__async_eventfd(void) { } if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); no_eventfd = 1; @@ -265,5 +265,5 @@ static int uv__async_eventfd(void) { #endif - return -ENOSYS; + return UV_ENOSYS; } diff --git a/deps/uv/src/unix/bsd-ifaddrs.c b/deps/uv/src/unix/bsd-ifaddrs.c index ea3166c5e977c7..0d0215448640a4 100644 --- a/deps/uv/src/unix/bsd-ifaddrs.c +++ b/deps/uv/src/unix/bsd-ifaddrs.c @@ -70,7 +70,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { int i; if (getifaddrs(&addrs) != 0) - return -errno; + return UV__ERR(errno); *count = 0; @@ -85,7 +85,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { if (*addresses == NULL) { freeifaddrs(addrs); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index c7e431e5295d1b..3741c1d06b2d79 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -191,14 +191,14 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { socklen_t len; if (handle == NULL || value == NULL) - return -EINVAL; + return UV_EINVAL; if (handle->type == UV_TCP || handle->type == UV_NAMED_PIPE) fd = uv__stream_fd((uv_stream_t*) handle); else if (handle->type == UV_UDP) fd = ((uv_udp_t *) handle)->io_watcher.fd; else - return -ENOTSUP; + return UV_ENOTSUP; len = sizeof(*value); @@ -208,7 +208,7 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { r = setsockopt(fd, SOL_SOCKET, optname, (const void*) value, len); if (r < 0) - return -errno; + return UV__ERR(errno); return 0; } @@ -418,12 +418,12 @@ int uv__socket(int domain, int type, int protocol) { return sockfd; if (errno != EINVAL) - return -errno; + return UV__ERR(errno); #endif sockfd = socket(domain, type, protocol); if (sockfd == -1) - return -errno; + return UV__ERR(errno); err = uv__nonblock(sockfd, 1); if (err == 0) @@ -487,7 +487,7 @@ int uv__accept(int sockfd) { continue; if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); no_accept4 = 1; skip: @@ -497,7 +497,7 @@ int uv__accept(int sockfd) { if (peerfd == -1) { if (errno == EINTR) continue; - return -errno; + return UV__ERR(errno); } err = uv__cloexec(peerfd, 1); @@ -523,8 +523,8 @@ int uv__close_nocheckstdio(int fd) { saved_errno = errno; rc = close(fd); if (rc == -1) { - rc = -errno; - if (rc == -EINTR || rc == -EINPROGRESS) + rc = UV__ERR(errno); + if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) rc = 0; /* The close is in progress, not an error. */ errno = saved_errno; } @@ -550,7 +550,7 @@ int uv__nonblock_ioctl(int fd, int set) { while (r == -1 && errno == EINTR); if (r) - return -errno; + return UV__ERR(errno); return 0; } @@ -565,7 +565,7 @@ int uv__cloexec_ioctl(int fd, int set) { while (r == -1 && errno == EINTR); if (r) - return -errno; + return UV__ERR(errno); return 0; } @@ -581,7 +581,7 @@ int uv__nonblock_fcntl(int fd, int set) { while (r == -1 && errno == EINTR); if (r == -1) - return -errno; + return UV__ERR(errno); /* Bail out now if already set/clear. */ if (!!(r & O_NONBLOCK) == !!set) @@ -597,7 +597,7 @@ int uv__nonblock_fcntl(int fd, int set) { while (r == -1 && errno == EINTR); if (r) - return -errno; + return UV__ERR(errno); return 0; } @@ -612,7 +612,7 @@ int uv__cloexec_fcntl(int fd, int set) { while (r == -1 && errno == EINTR); if (r == -1) - return -errno; + return UV__ERR(errno); /* Bail out now if already set/clear. */ if (!!(r & FD_CLOEXEC) == !!set) @@ -628,7 +628,7 @@ int uv__cloexec_fcntl(int fd, int set) { while (r == -1 && errno == EINTR); if (r) - return -errno; + return UV__ERR(errno); return 0; } @@ -643,7 +643,7 @@ int uv__dup(int fd) { fd = dup(fd); if (fd == -1) - return -errno; + return UV__ERR(errno); err = uv__cloexec(fd, 1); if (err) { @@ -667,10 +667,10 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { if (rc != -1) return rc; if (errno != EINVAL) - return -errno; + return UV__ERR(errno); rc = recvmsg(fd, msg, flags); if (rc == -1) - return -errno; + return UV__ERR(errno); no_msg_cmsg_cloexec = 1; } else { rc = recvmsg(fd, msg, flags); @@ -679,7 +679,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { rc = recvmsg(fd, msg, flags); #endif if (rc == -1) - return -errno; + return UV__ERR(errno); if (msg->msg_controllen == 0) return rc; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) @@ -695,10 +695,10 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { int uv_cwd(char* buffer, size_t* size) { if (buffer == NULL || size == NULL) - return -EINVAL; + return UV_EINVAL; if (getcwd(buffer, *size) == NULL) - return -errno; + return UV__ERR(errno); *size = strlen(buffer); if (*size > 1 && buffer[*size - 1] == '/') { @@ -712,7 +712,7 @@ int uv_cwd(char* buffer, size_t* size) { int uv_chdir(const char* dir) { if (chdir(dir)) - return -errno; + return UV__ERR(errno); return 0; } @@ -749,11 +749,11 @@ int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { break; default: - return -EINVAL; + return UV_EINVAL; } if (uv__is_closing(handle) || fd_out == -1) - return -EBADF; + return UV_EBADF; *fd = fd_out; return 0; @@ -931,7 +931,7 @@ int uv_getrusage(uv_rusage_t* rusage) { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage)) - return -errno; + return UV__ERR(errno); rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec; rusage->ru_utime.tv_usec = usage.ru_utime.tv_usec; @@ -973,7 +973,7 @@ int uv__open_cloexec(const char* path, int flags) { return fd; if (errno != EINVAL) - return -errno; + return UV__ERR(errno); /* O_CLOEXEC not supported. */ no_cloexec = 1; @@ -982,7 +982,7 @@ int uv__open_cloexec(const char* path, int flags) { fd = open(path, flags); if (fd == -1) - return -errno; + return UV__ERR(errno); err = uv__cloexec(fd, 1); if (err) { @@ -999,14 +999,14 @@ int uv__dup2_cloexec(int oldfd, int newfd) { #if (defined(__FreeBSD__) && __FreeBSD__ >= 10) || defined(__NetBSD__) r = dup3(oldfd, newfd, O_CLOEXEC); if (r == -1) - return -errno; + return UV__ERR(errno); return r; #elif defined(__FreeBSD__) && defined(F_DUP2FD_CLOEXEC) r = fcntl(oldfd, F_DUP2FD_CLOEXEC, newfd); if (r != -1) return r; if (errno != EINVAL) - return -errno; + return UV__ERR(errno); /* Fall through. */ #elif defined(__linux__) static int no_dup3; @@ -1017,7 +1017,7 @@ int uv__dup2_cloexec(int oldfd, int newfd) { if (r != -1) return r; if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); /* Fall through. */ no_dup3 = 1; } @@ -1033,7 +1033,7 @@ int uv__dup2_cloexec(int oldfd, int newfd) { #endif if (r == -1) - return -errno; + return UV__ERR(errno); err = uv__cloexec(newfd, 1); if (err) { @@ -1053,7 +1053,7 @@ int uv_os_homedir(char* buffer, size_t* size) { int r; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; /* Check if the HOME environment variable is set first */ buf = getenv("HOME"); @@ -1063,7 +1063,7 @@ int uv_os_homedir(char* buffer, size_t* size) { if (len >= *size) { *size = len + 1; - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, buf, len + 1); @@ -1084,7 +1084,7 @@ int uv_os_homedir(char* buffer, size_t* size) { if (len >= *size) { *size = len + 1; uv_os_free_passwd(&pwd); - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, pwd.homedir, len + 1); @@ -1100,7 +1100,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) { size_t len; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; #define CHECK_ENV_VAR(name) \ do { \ @@ -1130,7 +1130,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) { if (len >= *size) { *size = len + 1; - return -ENOBUFS; + return UV_ENOBUFS; } /* The returned directory should not have a trailing slash. */ @@ -1162,11 +1162,11 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { getpwuid_r = dlsym(RTLD_DEFAULT, "getpwuid_r"); if (getpwuid_r == NULL) - return -ENOSYS; + return UV_ENOSYS; #endif if (pwd == NULL) - return -EINVAL; + return UV_EINVAL; initsize = sysconf(_SC_GETPW_R_SIZE_MAX); @@ -1183,7 +1183,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { buf = uv__malloc(bufsize); if (buf == NULL) - return -ENOMEM; + return UV_ENOMEM; r = getpwuid_r(uid, &pw, buf, bufsize, &result); @@ -1200,7 +1200,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (result == NULL) { uv__free(buf); - return -ENOENT; + return UV_ENOENT; } /* Allocate memory for the username, shell, and home directory */ @@ -1211,7 +1211,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (pwd->username == NULL) { uv__free(buf); - return -ENOMEM; + return UV_ENOMEM; } /* Copy the username */ @@ -1267,18 +1267,18 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { size_t len; if (name == NULL || buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; var = getenv(name); if (var == NULL) - return -ENOENT; + return UV_ENOENT; len = strlen(var); if (len >= *size) { *size = len + 1; - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, var, len + 1); @@ -1290,10 +1290,10 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { int uv_os_setenv(const char* name, const char* value) { if (name == NULL || value == NULL) - return -EINVAL; + return UV_EINVAL; if (setenv(name, value, 1) != 0) - return -errno; + return UV__ERR(errno); return 0; } @@ -1301,10 +1301,10 @@ int uv_os_setenv(const char* name, const char* value) { int uv_os_unsetenv(const char* name) { if (name == NULL) - return -EINVAL; + return UV_EINVAL; if (unsetenv(name) != 0) - return -errno; + return UV__ERR(errno); return 0; } @@ -1321,17 +1321,17 @@ int uv_os_gethostname(char* buffer, size_t* size) { size_t len; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; if (gethostname(buf, sizeof(buf)) != 0) - return -errno; + return UV__ERR(errno); buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ len = strlen(buf); if (len >= *size) { *size = len + 1; - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, buf, len + 1); diff --git a/deps/uv/src/unix/cygwin.c b/deps/uv/src/unix/cygwin.c index 5a887dd4c2d03e..9fe4093ef46fb0 100644 --- a/deps/uv/src/unix/cygwin.c +++ b/deps/uv/src/unix/cygwin.c @@ -29,7 +29,7 @@ int uv_uptime(double* uptime) { struct sysinfo info; if (sysinfo(&info) < 0) - return -errno; + return UV__ERR(errno); *uptime = info.uptime; return 0; diff --git a/deps/uv/src/unix/darwin-proctitle.c b/deps/uv/src/unix/darwin-proctitle.c index 1142311609fb07..dabde2239ccab3 100644 --- a/deps/uv/src/unix/darwin-proctitle.c +++ b/deps/uv/src/unix/darwin-proctitle.c @@ -18,6 +18,9 @@ * IN THE SOFTWARE. */ +#include "uv.h" +#include "internal.h" + #include #include #include @@ -41,14 +44,14 @@ static int uv__pthread_setname_np(const char* name) { dlsym(RTLD_DEFAULT, "pthread_setname_np"); if (dynamic_pthread_setname_np == NULL) - return -ENOSYS; + return UV_ENOSYS; strncpy(namebuf, name, sizeof(namebuf) - 1); namebuf[sizeof(namebuf) - 1] = '\0'; err = dynamic_pthread_setname_np(namebuf); if (err) - return -err; + return UV__ERR(err); return 0; } @@ -84,7 +87,7 @@ int uv__set_process_title(const char* title) { CFTypeRef asn; int err; - err = -ENOENT; + err = UV_ENOENT; application_services_handle = dlopen("/System/Library/Frameworks/" "ApplicationServices.framework/" "Versions/A/ApplicationServices", @@ -151,7 +154,7 @@ int uv__set_process_title(const char* title) { /* Black 10.9 magic, to remove (Not responding) mark in Activity Monitor */ hi_services_bundle = pCFBundleGetBundleWithIdentifier(S("com.apple.HIServices")); - err = -ENOENT; + err = UV_ENOENT; if (hi_services_bundle == NULL) goto out; @@ -182,7 +185,7 @@ int uv__set_process_title(const char* title) { asn = pLSGetCurrentApplicationASN(); - err = -EINVAL; + err = UV_EINVAL; if (pLSSetApplicationInformationItem(-2, /* Magic value. */ asn, *display_name_key, diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index df6dd1c61bbc24..31ad8a9e48a077 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -37,7 +37,7 @@ int uv__platform_loop_init(uv_loop_t* loop) { loop->cf_state = NULL; if (uv__kqueue_init(loop)) - return -errno; + return UV__ERR(errno); return 0; } @@ -68,18 +68,18 @@ int uv_exepath(char* buffer, size_t* size) { size_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; exepath_size = sizeof(exepath); if (_NSGetExecutablePath(exepath, &exepath_size)) - return -EIO; + return UV_EIO; if (realpath(exepath, abspath) != abspath) - return -errno; + return UV__ERR(errno); abspath_size = strlen(abspath); if (abspath_size == 0) - return -EIO; + return UV_EIO; *size -= 1; if (*size > abspath_size) @@ -98,7 +98,7 @@ uint64_t uv_get_free_memory(void) { if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count) != KERN_SUCCESS) { - return -EINVAL; /* FIXME(bnoordhuis) Translate error. */ + return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ } return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); @@ -111,7 +111,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info; } @@ -158,7 +158,7 @@ int uv_uptime(double* uptime) { static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); now = time(NULL); *uptime = now - info.tv_sec; @@ -181,23 +181,23 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(model); if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && sysctlbyname("hw.model", &model, &size, NULL, 0)) { - return -errno; + return UV__ERR(errno); } size = sizeof(cpuspeed); if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, &msg_type) != KERN_SUCCESS) { - return -EINVAL; /* FIXME(bnoordhuis) Translate error. */ + return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ } *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) { vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); - return -ENOMEM; + return UV_ENOMEM; } *count = numcpus; diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index f2b3f247a05e3a..70ccb13042fddb 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -72,11 +72,11 @@ int uv_exepath(char* buffer, size_t* size) { ssize_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath)); if (abspath_size < 0) - return -errno; + return UV__ERR(errno); assert(abspath_size > 0); *size -= 1; @@ -96,7 +96,7 @@ int uv_exepath(char* buffer, size_t* size) { size_t abspath_size; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; mib[0] = CTL_KERN; mib[1] = KERN_PROC; @@ -105,7 +105,7 @@ int uv_exepath(char* buffer, size_t* size) { abspath_size = sizeof abspath; if (sysctl(mib, 4, abspath, &abspath_size, NULL, 0)) - return -errno; + return UV__ERR(errno); assert(abspath_size > 0); abspath_size -= 1; @@ -126,7 +126,7 @@ uint64_t uv_get_free_memory(void) { size_t size = sizeof(freecount); if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) freecount * sysconf(_SC_PAGESIZE); @@ -140,7 +140,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info; } @@ -176,7 +176,7 @@ int uv_set_process_title(const char* title) { if (process_title == NULL) { uv_mutex_unlock(&process_title_mutex); - return -ENOMEM; + return UV_ENOMEM; } uv__free(process_title); @@ -204,7 +204,7 @@ int uv_get_process_title(char* buffer, size_t size) { size_t len; if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -214,7 +214,7 @@ int uv_get_process_title(char* buffer, size_t size) { if (size < len) { uv_mutex_unlock(&process_title_mutex); - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, process_title, len); @@ -243,7 +243,7 @@ int uv_resident_set_memory(size_t* rss) { kinfo_size = sizeof(kinfo); if (sysctl(mib, 4, &kinfo, &kinfo_size, NULL, 0)) - return -errno; + return UV__ERR(errno); page_size = getpagesize(); @@ -262,7 +262,7 @@ int uv_uptime(double* uptime) { struct timespec sp; r = clock_gettime(CLOCK_MONOTONIC, &sp); if (r) - return -errno; + return UV__ERR(errno); *uptime = sp.tv_sec; return 0; @@ -309,15 +309,15 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(model); if (sysctlbyname(model_key, &model, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); size = sizeof(numcpus); if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) - return -ENOMEM; + return UV_ENOMEM; *count = numcpus; @@ -327,7 +327,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(maxcpus); if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) { uv__free(*cpu_infos); - return -errno; + return UV__ERR(errno); } size = maxcpus * CPUSTATES * sizeof(long); @@ -335,13 +335,13 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { cp_times = uv__malloc(size); if (cp_times == NULL) { uv__free(*cpu_infos); - return -ENOMEM; + return UV_ENOMEM; } if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) { uv__free(cp_times); uv__free(*cpu_infos); - return -errno; + return UV__ERR(errno); } for (i = 0; i < numcpus; i++) { diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index e0969a4c2f3d91..92e2d255702fd7 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -67,16 +67,15 @@ #define INIT(subtype) \ do { \ if (req == NULL) \ - return -EINVAL; \ - req->type = UV_FS; \ - if (cb != NULL) \ - uv__req_init(loop, req, UV_FS); \ + return UV_EINVAL; \ + UV_REQ_INIT(req, UV_FS); \ req->fs_type = UV_FS_ ## subtype; \ req->result = 0; \ req->ptr = NULL; \ req->loop = loop; \ req->path = NULL; \ req->new_path = NULL; \ + req->bufs = NULL; \ req->cb = cb; \ } \ while (0) @@ -88,10 +87,8 @@ req->path = path; \ } else { \ req->path = uv__strdup(path); \ - if (req->path == NULL) { \ - uv__req_unregister(loop, req); \ - return -ENOMEM; \ - } \ + if (req->path == NULL) \ + return UV_ENOMEM; \ } \ } \ while (0) @@ -107,10 +104,8 @@ path_len = strlen(path) + 1; \ new_path_len = strlen(new_path) + 1; \ req->path = uv__malloc(path_len + new_path_len); \ - if (req->path == NULL) { \ - uv__req_unregister(loop, req); \ - return -ENOMEM; \ - } \ + if (req->path == NULL) \ + return UV_ENOMEM; \ req->new_path = req->path + path_len; \ memcpy((void*) req->path, path, path_len); \ memcpy((void*) req->new_path, new_path, new_path_len); \ @@ -121,6 +116,7 @@ #define POST \ do { \ if (cb != NULL) { \ + uv__req_register(loop, req); \ uv__work_submit(loop, &req->work_req, uv__fs_work, uv__fs_done); \ return 0; \ } \ @@ -818,7 +814,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { /* Get the source file's mode. */ if (fstat(srcfd, &statsbuf)) { - err = -errno; + err = UV__ERR(errno); goto out; } @@ -842,7 +838,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { } if (fchmod(dstfd, statsbuf.st_mode) == -1) { - err = -errno; + err = UV__ERR(errno); goto out; } @@ -1107,7 +1103,7 @@ static void uv__fs_work(struct uv__work* w) { } while (r == -1 && errno == EINTR && retry_on_eintr); if (r == -1) - req->result = -errno; + req->result = UV__ERR(errno); else req->result = r; @@ -1125,9 +1121,9 @@ static void uv__fs_done(struct uv__work* w, int status) { req = container_of(w, uv_fs_t, work_req); uv__req_unregister(req->loop, req); - if (status == -ECANCELED) { + if (status == UV_ECANCELED) { assert(req->result == 0); - req->result = -ECANCELED; + req->result = UV_ECANCELED; } req->cb(req); @@ -1288,11 +1284,8 @@ int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_cb cb) { INIT(MKDTEMP); req->path = uv__strdup(tpl); - if (req->path == NULL) { - if (cb != NULL) - uv__req_unregister(loop, req); - return -ENOMEM; - } + if (req->path == NULL) + return UV_ENOMEM; POST; } @@ -1320,7 +1313,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, INIT(READ); if (bufs == NULL || nbufs == 0) - return -EINVAL; + return UV_EINVAL; req->file = file; @@ -1329,11 +1322,8 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->bufs == NULL) { - if (cb != NULL) - uv__req_unregister(loop, req); - return -ENOMEM; - } + if (req->bufs == NULL) + return UV_ENOMEM; memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); @@ -1459,7 +1449,7 @@ int uv_fs_write(uv_loop_t* loop, INIT(WRITE); if (bufs == NULL || nbufs == 0) - return -EINVAL; + return UV_EINVAL; req->file = file; @@ -1468,11 +1458,8 @@ int uv_fs_write(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->bufs == NULL) { - if (cb != NULL) - uv__req_unregister(loop, req); - return -ENOMEM; - } + if (req->bufs == NULL) + return UV_ENOMEM; memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); @@ -1499,6 +1486,10 @@ void uv_fs_req_cleanup(uv_fs_t* req) { if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + if (req->ptr != &req->statbuf) uv__free(req->ptr); req->ptr = NULL; @@ -1514,7 +1505,7 @@ int uv_fs_copyfile(uv_loop_t* loop, INIT(COPYFILE); if (flags & ~UV_FS_COPYFILE_EXCL) - return -EINVAL; + return UV_EINVAL; PATH2; req->flags = flags; diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index 38837406a66f68..47d8024b6d51ba 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -379,7 +379,7 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { if (!pFSEventStreamStart(ref)) { pFSEventStreamInvalidate(ref); pFSEventStreamRelease(ref); - return -EMFILE; + return UV_EMFILE; } state->fsevent_stream = ref; @@ -440,7 +440,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, uv__fsevents_destroy_stream(handle->loop); /* Any failure below will be a memory failure */ - err = -ENOMEM; + err = UV_ENOMEM; /* Create list of all watched paths */ uv_mutex_lock(&state->fsevent_mutex); @@ -474,7 +474,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, /* Create new FSEventStream */ cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL); if (cf_paths == NULL) { - err = -ENOMEM; + err = UV_ENOMEM; goto final; } err = uv__fsevents_create_stream(handle->loop, cf_paths); @@ -528,7 +528,7 @@ static int uv__fsevents_global_init(void) { * but if it ever becomes one, we can turn the dynamic library handles into * per-event loop properties and have the dynamic linker keep track for us. */ - err = -ENOSYS; + err = UV_ENOSYS; core_foundation_handle = dlopen("/System/Library/Frameworks/" "CoreFoundation.framework/" "Versions/A/CoreFoundation", @@ -543,7 +543,7 @@ static int uv__fsevents_global_init(void) { if (core_services_handle == NULL) goto out; - err = -ENOENT; + err = UV_ENOENT; #define V(handle, symbol) \ do { \ *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ @@ -607,7 +607,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { state = uv__calloc(1, sizeof(*state)); if (state == NULL) - return -ENOMEM; + return UV_ENOMEM; err = uv_mutex_init(&loop->cf_mutex); if (err) @@ -636,7 +636,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { ctx.perform = uv__cf_loop_cb; state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx); if (state->signal_source == NULL) { - err = -ENOMEM; + err = UV_ENOMEM; goto fail_signal_source_create; } @@ -655,7 +655,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { loop->cf_state = state; /* uv_thread_t is an alias for pthread_t. */ - err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop); + err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop)); if (attr != NULL) pthread_attr_destroy(attr); @@ -787,7 +787,7 @@ int uv__cf_loop_signal(uv_loop_t* loop, item = uv__malloc(sizeof(*item)); if (item == NULL) - return -ENOMEM; + return UV_ENOMEM; item->handle = handle; item->type = type; @@ -817,7 +817,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { /* Get absolute path to file */ handle->realpath = realpath(handle->path, NULL); if (handle->realpath == NULL) - return -errno; + return UV__ERR(errno); handle->realpath_len = strlen(handle->realpath); /* Initialize event queue */ @@ -830,7 +830,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { */ handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb)); if (handle->cf_cb == NULL) { - err = -ENOMEM; + err = UV_ENOMEM; goto fail_cf_cb_malloc; } @@ -881,7 +881,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) { uv__cf_loop_state_t* state; if (handle->cf_cb == NULL) - return -EINVAL; + return UV_EINVAL; /* Remove handle from the list */ state = handle->loop->cf_state; @@ -895,7 +895,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) { assert(handle != NULL); err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing); if (err) - return -err; + return UV__ERR(err); /* Wait for deinitialization */ uv_sem_wait(&state->fsevent_sem); diff --git a/deps/uv/src/unix/getaddrinfo.c b/deps/uv/src/unix/getaddrinfo.c index 0185971697a95d..10e8afd75e831b 100644 --- a/deps/uv/src/unix/getaddrinfo.c +++ b/deps/uv/src/unix/getaddrinfo.c @@ -86,7 +86,7 @@ int uv__getaddrinfo_translate_error(int sys_err) { case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE; #endif #if defined(EAI_SYSTEM) - case EAI_SYSTEM: return -errno; + case EAI_SYSTEM: return UV__ERR(errno); #endif } assert(!"unknown EAI_* error code"); @@ -125,7 +125,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) { req->service = NULL; req->hostname = NULL; - if (status == -ECANCELED) { + if (status == UV_ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; } @@ -148,7 +148,7 @@ int uv_getaddrinfo(uv_loop_t* loop, char* buf; if (req == NULL || (hostname == NULL && service == NULL)) - return -EINVAL; + return UV_EINVAL; hostname_len = hostname ? strlen(hostname) + 1 : 0; service_len = service ? strlen(service) + 1 : 0; @@ -156,7 +156,7 @@ int uv_getaddrinfo(uv_loop_t* loop, buf = uv__malloc(hostname_len + service_len + hints_len); if (buf == NULL) - return -ENOMEM; + return UV_ENOMEM; uv__req_init(loop, req, UV_GETADDRINFO); req->loop = loop; @@ -211,7 +211,7 @@ int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { return UV_EINVAL; if (if_indextoname(ifindex, ifname_buf) == NULL) - return -errno; + return UV__ERR(errno); len = strnlen(ifname_buf, sizeof(ifname_buf)); diff --git a/deps/uv/src/unix/getnameinfo.c b/deps/uv/src/unix/getnameinfo.c index daa798a450e25c..9a4367224c7fa6 100644 --- a/deps/uv/src/unix/getnameinfo.c +++ b/deps/uv/src/unix/getnameinfo.c @@ -61,7 +61,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) { uv__req_unregister(req->loop, req); host = service = NULL; - if (status == -ECANCELED) { + if (status == UV_ECANCELED) { assert(req->retcode == 0); req->retcode = UV_EAI_CANCELED; } else if (req->retcode == 0) { diff --git a/deps/uv/src/unix/ibmi.c b/deps/uv/src/unix/ibmi.c index 8380d02db7616d..c50a4e76f84119 100644 --- a/deps/uv/src/unix/ibmi.c +++ b/deps/uv/src/unix/ibmi.c @@ -92,7 +92,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t)); if (!*cpu_infos) { - return -ENOMEM; + return UV_ENOMEM; } cpu_info = *cpu_infos; diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 3df5c4c3eb8619..2bb3773c068d8e 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -29,6 +29,7 @@ #include /* strrchr */ #include /* O_CLOEXEC, may be */ #include +#include #if defined(__STRICT_ANSI__) # define inline __inline diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 5e89bdced4e5f3..a30fd730ff872e 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -51,7 +51,7 @@ static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); int uv__kqueue_init(uv_loop_t* loop) { loop->backend_fd = kqueue(); if (loop->backend_fd == -1) - return -errno; + return UV__ERR(errno); uv__cloexec(loop->backend_fd, 1); @@ -98,7 +98,7 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { rc = 0; EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) - rc = -errno; + rc = UV__ERR(errno); EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); if (rc == 0) @@ -458,12 +458,12 @@ int uv_fs_event_start(uv_fs_event_t* handle, int fd; if (uv__is_active(handle)) - return -EINVAL; + return UV_EINVAL; /* TODO open asynchronously - but how do we report back errors? */ fd = open(path, O_RDONLY); if (fd == -1) - return -errno; + return UV__ERR(errno); uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__fs_event, fd); diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 4d480ce10a2585..b63c25f3c2258f 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -101,7 +101,7 @@ int uv__platform_loop_init(uv_loop_t* loop) { loop->inotify_watchers = NULL; if (fd == -1) - return -errno; + return UV__ERR(errno); return 0; } @@ -175,7 +175,7 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { rc = 0; if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_ADD, fd, &e)) if (errno != EEXIST) - rc = -errno; + rc = UV__ERR(errno); if (rc == 0) if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, &e)) @@ -485,7 +485,7 @@ int uv_resident_set_memory(size_t* rss) { while (fd == -1 && errno == EINTR); if (fd == -1) - return -errno; + return UV__ERR(errno); do n = read(fd, buf, sizeof(buf) - 1); @@ -493,7 +493,7 @@ int uv_resident_set_memory(size_t* rss) { uv__close(fd); if (n == -1) - return -errno; + return UV__ERR(errno); buf[n] = '\0'; s = strchr(buf, ' '); @@ -525,7 +525,7 @@ int uv_resident_set_memory(size_t* rss) { return 0; err: - return -EINVAL; + return UV_EINVAL; } @@ -547,7 +547,7 @@ int uv_uptime(double* uptime) { } if (r) - return -errno; + return UV__ERR(errno); *uptime = now.tv_sec; return 0; @@ -559,7 +559,7 @@ static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) { char buf[1024]; if (!fgets(buf, sizeof(buf), statfile_fp)) - return -EIO; + return UV_EIO; num = 0; while (fgets(buf, sizeof(buf), statfile_fp)) { @@ -569,7 +569,7 @@ static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) { } if (num == 0) - return -EIO; + return UV_EIO; *numcpus = num; return 0; @@ -587,13 +587,13 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { statfile_fp = uv__open_file("/proc/stat"); if (statfile_fp == NULL) - return -errno; + return UV__ERR(errno); err = uv__cpu_num(statfile_fp, &numcpus); if (err < 0) goto out; - err = -ENOMEM; + err = UV_ENOMEM; ci = uv__calloc(numcpus, sizeof(*ci)); if (ci == NULL) goto out; @@ -667,7 +667,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { defined(__x86_64__) fp = uv__open_file("/proc/cpuinfo"); if (fp == NULL) - return -errno; + return UV__ERR(errno); while (fgets(buf, sizeof(buf), fp)) { if (model_idx < numcpus) { @@ -676,7 +676,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ if (model == NULL) { fclose(fp); - return -ENOMEM; + return UV_ENOMEM; } ci[model_idx++].model = model; continue; @@ -695,7 +695,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ if (model == NULL) { fclose(fp); - return -ENOMEM; + return UV_ENOMEM; } ci[model_idx++].model = model; continue; @@ -725,7 +725,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { while (model_idx < numcpus) { model = uv__strndup(inferred_model, strlen(inferred_model)); if (model == NULL) - return -ENOMEM; + return UV_ENOMEM; ci[model_idx++].model = model; } @@ -854,7 +854,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { #ifndef HAVE_IFADDRS_H - return -ENOSYS; + return UV_ENOSYS; #else struct ifaddrs *addrs, *ent; uv_interface_address_t* address; @@ -862,7 +862,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, struct sockaddr_ll *sll; if (getifaddrs(&addrs)) - return -errno; + return UV__ERR(errno); *count = 0; *addresses = NULL; @@ -881,7 +881,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; diff --git a/deps/uv/src/unix/linux-inotify.c b/deps/uv/src/unix/linux-inotify.c index 5934c5d8cb060e..bcad630fabf112 100644 --- a/deps/uv/src/unix/linux-inotify.c +++ b/deps/uv/src/unix/linux-inotify.c @@ -73,11 +73,11 @@ static int new_inotify_fd(void) { return fd; if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); fd = uv__inotify_init(); if (fd == -1) - return -errno; + return UV__ERR(errno); err = uv__cloexec(fd, 1); if (err == 0) @@ -283,7 +283,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, int wd; if (uv__is_active(handle)) - return -EINVAL; + return UV_EINVAL; err = init_inotify(handle->loop); if (err) @@ -300,7 +300,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, wd = uv__inotify_add_watch(handle->loop->inotify_fd, path, events); if (wd == -1) - return -errno; + return UV__ERR(errno); w = find_watcher(handle->loop, wd); if (w) @@ -308,7 +308,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, w = uv__malloc(sizeof(*w) + strlen(path) + 1); if (w == NULL) - return -ENOMEM; + return UV_ENOMEM; w->wd = wd; w->path = strcpy((char*)(w + 1), path); diff --git a/deps/uv/src/unix/loop-watcher.c b/deps/uv/src/unix/loop-watcher.c index 340bb0dfa11350..b8c1c2a7102ac3 100644 --- a/deps/uv/src/unix/loop-watcher.c +++ b/deps/uv/src/unix/loop-watcher.c @@ -31,7 +31,7 @@ \ int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ if (uv__is_active(handle)) return 0; \ - if (cb == NULL) return -EINVAL; \ + if (cb == NULL) return UV_EINVAL; \ QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \ handle->name##_cb = cb; \ uv__handle_start(handle); \ diff --git a/deps/uv/src/unix/netbsd.c b/deps/uv/src/unix/netbsd.c index 742507233144a1..2605c114bca72d 100644 --- a/deps/uv/src/unix/netbsd.c +++ b/deps/uv/src/unix/netbsd.c @@ -82,7 +82,7 @@ int uv_exepath(char* buffer, size_t* size) { int mib[4]; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; @@ -91,7 +91,7 @@ int uv_exepath(char* buffer, size_t* size) { int_size = ARRAY_SIZE(int_buf); if (sysctl(mib, 4, int_buf, &int_size, NULL, 0)) - return -errno; + return UV__ERR(errno); /* Copy string from the intermediate buffer to outer one with appropriate * length. @@ -111,7 +111,7 @@ uint64_t uv_get_free_memory(void) { int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -128,7 +128,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info; } @@ -150,7 +150,7 @@ int uv_set_process_title(const char* title) { if (process_title == NULL) { uv_mutex_unlock(&process_title_mutex); - return -ENOMEM; + return UV_ENOMEM; } uv__free(process_title); @@ -167,7 +167,7 @@ int uv_get_process_title(char* buffer, size_t size) { size_t len; if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -177,7 +177,7 @@ int uv_get_process_title(char* buffer, size_t size) { if (size < len) { uv_mutex_unlock(&process_title_mutex); - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, process_title, len); @@ -219,7 +219,7 @@ int uv_resident_set_memory(size_t* rss) { error: if (kd) kvm_close(kd); - return -EPERM; + return UV_EPERM; } @@ -230,7 +230,7 @@ int uv_uptime(double* uptime) { static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); now = time(NULL); @@ -254,12 +254,12 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(model); if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) && sysctlbyname("hw.model", &model, &size, NULL, 0)) { - return -errno; + return UV__ERR(errno); } size = sizeof(numcpus); if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); *count = numcpus; /* Only i386 and amd64 have machdep.tsc_freq */ @@ -270,16 +270,16 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = numcpus * CPUSTATES * sizeof(*cp_times); cp_times = uv__malloc(size); if (cp_times == NULL) - return -ENOMEM; + return UV_ENOMEM; if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) { uv__free(cp_times); uv__free(*cpu_infos); - return -ENOMEM; + return UV_ENOMEM; } for (i = 0; i < numcpus; i++) { diff --git a/deps/uv/src/unix/no-fsevents.c b/deps/uv/src/unix/no-fsevents.c index 38fb6ab0bb2c16..158643af1ef528 100644 --- a/deps/uv/src/unix/no-fsevents.c +++ b/deps/uv/src/unix/no-fsevents.c @@ -25,16 +25,16 @@ #include int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { - return -ENOSYS; + return UV_ENOSYS; } int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* filename, unsigned int flags) { - return -ENOSYS; + return UV_ENOSYS; } int uv_fs_event_stop(uv_fs_event_t* handle) { - return -ENOSYS; + return UV_ENOSYS; } void uv__fs_event_close(uv_fs_event_t* handle) { diff --git a/deps/uv/src/unix/no-proctitle.c b/deps/uv/src/unix/no-proctitle.c index a5c19fbff58d38..165740ca3ff9d5 100644 --- a/deps/uv/src/unix/no-proctitle.c +++ b/deps/uv/src/unix/no-proctitle.c @@ -35,7 +35,7 @@ int uv_set_process_title(const char* title) { int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; buffer[0] = '\0'; return 0; diff --git a/deps/uv/src/unix/openbsd.c b/deps/uv/src/unix/openbsd.c index c0ffa564b4c2e9..ce937cd3efec25 100644 --- a/deps/uv/src/unix/openbsd.c +++ b/deps/uv/src/unix/openbsd.c @@ -78,11 +78,11 @@ int uv_exepath(char* buffer, size_t* size) { int err; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; mypid = getpid(); for (;;) { - err = -ENOMEM; + err = UV_ENOMEM; argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size); if (argsbuf_tmp == NULL) goto out; @@ -95,14 +95,14 @@ int uv_exepath(char* buffer, size_t* size) { break; } if (errno != ENOMEM) { - err = -errno; + err = UV__ERR(errno); goto out; } argsbuf_size *= 2U; } if (argsbuf[0] == NULL) { - err = -EINVAL; /* FIXME(bnoordhuis) More appropriate error. */ + err = UV_EINVAL; /* FIXME(bnoordhuis) More appropriate error. */ goto out; } @@ -128,7 +128,7 @@ uint64_t uv_get_free_memory(void) { int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -140,7 +140,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); return (uint64_t) info; } @@ -162,7 +162,7 @@ int uv_set_process_title(const char* title) { if (process_title == NULL) { uv_mutex_unlock(&process_title_mutex); - return -ENOMEM; + return UV_ENOMEM; } uv__free(process_title); @@ -179,7 +179,7 @@ int uv_get_process_title(char* buffer, size_t size) { size_t len; if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -189,7 +189,7 @@ int uv_get_process_title(char* buffer, size_t size) { if (size < len) { uv_mutex_unlock(&process_title_mutex); - return -ENOBUFS; + return UV_ENOBUFS; } memcpy(buffer, process_title, len); @@ -219,7 +219,7 @@ int uv_resident_set_memory(size_t* rss) { mib[5] = 1; if (sysctl(mib, 6, &kinfo, &size, NULL, 0) < 0) - return -errno; + return UV__ERR(errno); *rss = kinfo.p_vm_rssize * page_size; return 0; @@ -233,7 +233,7 @@ int uv_uptime(double* uptime) { static int which[] = {CTL_KERN, KERN_BOOTTIME}; if (sysctl(which, 2, &info, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); now = time(NULL); @@ -255,16 +255,16 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(model); if (sysctl(which, 2, &model, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); which[1] = HW_NCPU; size = sizeof(numcpus); if (sysctl(which, 2, &numcpus, &size, NULL, 0)) - return -errno; + return UV__ERR(errno); *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); if (!(*cpu_infos)) - return -ENOMEM; + return UV_ENOMEM; *count = numcpus; @@ -272,7 +272,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(cpuspeed); if (sysctl(which, 2, &cpuspeed, &size, NULL, 0)) { uv__free(*cpu_infos); - return -errno; + return UV__ERR(errno); } size = sizeof(info); @@ -283,7 +283,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { size = sizeof(info); if (sysctl(which, 3, &info, &size, NULL, 0)) { uv__free(*cpu_infos); - return -errno; + return UV__ERR(errno); } cpu_info = &(*cpu_infos)[i]; diff --git a/deps/uv/src/unix/os390.c b/deps/uv/src/unix/os390.c index 081438e8e73d3c..f766b393395ee7 100644 --- a/deps/uv/src/unix/os390.c +++ b/deps/uv/src/unix/os390.c @@ -122,7 +122,7 @@ int uv__platform_loop_init(uv_loop_t* loop) { ep = epoll_create1(0); loop->ep = ep; if (ep == NULL) - return -errno; + return UV__ERR(errno); return 0; } @@ -259,12 +259,12 @@ int uv_exepath(char* buffer, size_t* size) { int pid; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; pid = getpid(); res = getexe(pid, args, sizeof(args)); if (res < 0) - return -EINVAL; + return UV_EINVAL; /* * Possibilities for args: @@ -277,7 +277,7 @@ int uv_exepath(char* buffer, size_t* size) { /* Case i) and ii) absolute or relative paths */ if (strchr(args, '/') != NULL) { if (realpath(args, abspath) != abspath) - return -errno; + return UV__ERR(errno); abspath_size = strlen(abspath); @@ -297,11 +297,11 @@ int uv_exepath(char* buffer, size_t* size) { char* path = getenv("PATH"); if (path == NULL) - return -EINVAL; + return UV_EINVAL; clonedpath = uv__strdup(path); if (clonedpath == NULL) - return -ENOMEM; + return UV_ENOMEM; token = strtok(clonedpath, ":"); while (token != NULL) { @@ -327,7 +327,7 @@ int uv_exepath(char* buffer, size_t* size) { uv__free(clonedpath); /* Out of tokens (path entries), and no match found */ - return -EINVAL; + return UV_EINVAL; } } @@ -407,7 +407,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { *cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t)); if (!*cpu_infos) - return -ENOMEM; + return UV_ENOMEM; cpu_info = *cpu_infos; idx = 0; @@ -452,7 +452,7 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, maxsize = 16384; if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) - return -errno; + return UV__ERR(errno); ifc.__nif6h_version = 1; ifc.__nif6h_buflen = maxsize; @@ -460,7 +460,7 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } @@ -484,7 +484,7 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); if (!(*addresses)) { uv__close(sockfd); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; @@ -543,13 +543,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (0 > sockfd) - return -errno; + return UV__ERR(errno); ifc.ifc_req = uv__calloc(1, maxsize); ifc.ifc_len = maxsize; if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } #define MAX(a,b) (((a)>(b))?(a):(b)) @@ -569,7 +569,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) @@ -584,7 +584,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { if (!(*addresses)) { uv__close(sockfd); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; @@ -607,7 +607,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { uv__close(sockfd); - return -ENOSYS; + return UV_ENOSYS; } if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) @@ -706,7 +706,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, int rc; if (uv__is_active(handle)) - return -EINVAL; + return UV_EINVAL; ep = handle->loop->ep; assert(ep->msg_queue != -1); @@ -718,11 +718,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, path = uv__strdup(filename); if (path == NULL) - return -ENOMEM; + return UV_ENOMEM; rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); if (rc != 0) - return -errno; + return UV__ERR(errno); uv__handle_start(handle); handle->path = path; diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index ac7cfb46a927fa..e640bf29fc1754 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -50,12 +50,12 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* Already bound? */ if (uv__stream_fd(handle) >= 0) - return -EINVAL; + return UV_EINVAL; /* Make a copy of the file name, it outlives this function's scope. */ pipe_fname = uv__strdup(name); if (pipe_fname == NULL) - return -ENOMEM; + return UV_ENOMEM; /* We've got a copy, don't touch the original any more. */ name = NULL; @@ -71,10 +71,10 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { saddr.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { - err = -errno; + err = UV__ERR(errno); /* Convert ENOENT to EACCES for compatibility with Windows. */ - if (err == -ENOENT) - err = -EACCES; + if (err == UV_ENOENT) + err = UV_EACCES; uv__close(sockfd); goto err_socket; @@ -94,7 +94,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { if (uv__stream_fd(handle) == -1) - return -EINVAL; + return UV_EINVAL; #if defined(__MVS__) /* On zOS, backlog=0 has undefined behaviour */ @@ -105,7 +105,7 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { #endif if (listen(uv__stream_fd(handle), backlog)) - return -errno; + return UV__ERR(errno); handle->connection_cb = cb; handle->io_watcher.cb = uv__server_io; @@ -180,14 +180,14 @@ void uv_pipe_connect(uv_connect_t* req, while (r == -1 && errno == EINTR); if (r == -1 && errno != EINPROGRESS) { - err = -errno; + err = UV__ERR(errno); #if defined(__CYGWIN__) || defined(__MSYS__) /* EBADF is supposed to mean that the socket fd is bad, but Cygwin reports EBADF instead of ENOTSOCK when the file is not a socket. We do not expect to see a bad fd here (e.g. due to new_sock), so translate the error. */ - if (err == -EBADF) - err = -ENOTSOCK; + if (err == UV_EBADF) + err = UV_ENOTSOCK; #endif goto out; } @@ -234,7 +234,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen); if (err < 0) { *size = 0; - return -errno; + return UV__ERR(errno); } #if defined(__linux__) @@ -312,15 +312,15 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { int r; if (handle == NULL || uv__stream_fd(handle) == -1) - return -EBADF; + return UV_EBADF; if (mode != UV_READABLE && mode != UV_WRITABLE && mode != (UV_WRITABLE | UV_READABLE)) - return -EINVAL; + return UV_EINVAL; if (fstat(uv__stream_fd(handle), &pipe_stat) == -1) - return -errno; + return UV__ERR(errno); desired_mode = 0; if (mode & UV_READABLE) @@ -353,5 +353,5 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { r = chmod(name_buffer, pipe_stat.st_mode); uv__free(name_buffer); - return r != -1 ? 0 : -errno; + return r != -1 ? 0 : UV__ERR(errno); } diff --git a/deps/uv/src/unix/poll.c b/deps/uv/src/unix/poll.c index 816c7dc2eb9e0b..f3b0bf4e433942 100644 --- a/deps/uv/src/unix/poll.c +++ b/deps/uv/src/unix/poll.c @@ -47,7 +47,7 @@ static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { if ((events & POLLERR) && !(events & UV__POLLPRI)) { uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); uv__handle_stop(handle); - handle->poll_cb(handle, -EBADF, 0); + handle->poll_cb(handle, UV_EBADF, 0); return; } @@ -76,7 +76,7 @@ int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { * Workaround for e.g. kqueue fds not supporting ioctls. */ err = uv__nonblock(fd, 1); - if (err == -ENOTTY) + if (err == UV_ENOTTY) if (uv__nonblock == uv__nonblock_ioctl) err = uv__nonblock_fcntl(fd, 1); diff --git a/deps/uv/src/unix/posix-poll.c b/deps/uv/src/unix/posix-poll.c index 3fba96e1bf154d..f356e76c79daec 100644 --- a/deps/uv/src/unix/posix-poll.c +++ b/deps/uv/src/unix/posix-poll.c @@ -315,10 +315,10 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { while (rv == -1 && (errno == EINTR || errno == EAGAIN)); if (rv == -1) - return -errno; + return UV__ERR(errno); if (p[0].revents & POLLNVAL) - return -EINVAL; + return UV_EINVAL; return 0; } diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 9842710d0ea159..74113e3a696cc2 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -126,7 +126,7 @@ int uv__make_socketpair(int fds[2], int flags) { * Anything else is a genuine error. */ if (errno != EINVAL) - return -errno; + return UV__ERR(errno); no_cloexec = 1; @@ -134,7 +134,7 @@ int uv__make_socketpair(int fds[2], int flags) { #endif if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - return -errno; + return UV__ERR(errno); uv__cloexec(fds[0], 1); uv__cloexec(fds[1], 1); @@ -159,7 +159,7 @@ int uv__make_pipe(int fds[2], int flags) { return 0; if (errno != ENOSYS) - return -errno; + return UV__ERR(errno); no_pipe2 = 1; @@ -167,7 +167,7 @@ int uv__make_pipe(int fds[2], int flags) { #endif if (pipe(fds)) - return -errno; + return UV__ERR(errno); uv__cloexec(fds[0], 1); uv__cloexec(fds[1], 1); @@ -198,7 +198,7 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { case UV_CREATE_PIPE: assert(container->data.stream != NULL); if (container->data.stream->type != UV_NAMED_PIPE) - return -EINVAL; + return UV_EINVAL; else return uv__make_socketpair(fds, 0); @@ -210,14 +210,14 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { fd = uv__stream_fd(container->data.stream); if (fd == -1) - return -EINVAL; + return UV_EINVAL; fds[1] = fd; return 0; default: assert(0 && "Unexpected flags"); - return -EINVAL; + return UV_EINVAL; } } @@ -299,7 +299,7 @@ static void uv__process_child_init(const uv_process_options_t* options, continue; pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); if (pipes[fd][1] == -1) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } } @@ -319,7 +319,7 @@ static void uv__process_child_init(const uv_process_options_t* options, close_fd = use_fd; if (use_fd == -1) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } } @@ -331,7 +331,7 @@ static void uv__process_child_init(const uv_process_options_t* options, fd = dup2(use_fd, fd); if (fd == -1) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } @@ -350,7 +350,7 @@ static void uv__process_child_init(const uv_process_options_t* options, } if (options->cwd != NULL && chdir(options->cwd)) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } @@ -366,12 +366,12 @@ static void uv__process_child_init(const uv_process_options_t* options, } if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } @@ -391,7 +391,7 @@ static void uv__process_child_init(const uv_process_options_t* options, if (SIG_ERR != signal(n, SIG_DFL)) continue; - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } @@ -400,12 +400,12 @@ static void uv__process_child_init(const uv_process_options_t* options, err = pthread_sigmask(SIG_SETMASK, &set, NULL); if (err != 0) { - uv__write_int(error_fd, -err); + uv__write_int(error_fd, UV__ERR(err)); _exit(127); } execvp(options->file, options->args); - uv__write_int(error_fd, -errno); + uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } #endif @@ -416,7 +416,7 @@ int uv_spawn(uv_loop_t* loop, const uv_process_options_t* options) { #if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ - return -ENOSYS; + return UV_ENOSYS; #else int signal_pipe[2] = { -1, -1 }; int pipes_storage[8][2]; @@ -443,7 +443,7 @@ int uv_spawn(uv_loop_t* loop, if (stdio_count < 3) stdio_count = 3; - err = -ENOMEM; + err = UV_ENOMEM; pipes = pipes_storage; if (stdio_count > (int) ARRAY_SIZE(pipes_storage)) pipes = uv__malloc(stdio_count * sizeof(*pipes)); @@ -493,7 +493,7 @@ int uv_spawn(uv_loop_t* loop, pid = fork(); if (pid == -1) { - err = -errno; + err = UV__ERR(errno); uv_rwlock_wrunlock(&loop->cloexec_lock); uv__close(signal_pipe[0]); uv__close(signal_pipe[1]); @@ -585,7 +585,7 @@ int uv_process_kill(uv_process_t* process, int signum) { int uv_kill(int pid, int signum) { if (kill(pid, signum)) - return -errno; + return UV__ERR(errno); else return 0; } diff --git a/deps/uv/src/unix/procfs-exepath.c b/deps/uv/src/unix/procfs-exepath.c index 5fdb6115505661..00dc021f21e71f 100644 --- a/deps/uv/src/unix/procfs-exepath.c +++ b/deps/uv/src/unix/procfs-exepath.c @@ -29,14 +29,14 @@ int uv_exepath(char* buffer, size_t* size) { ssize_t n; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; n = *size - 1; if (n > 0) n = readlink("/proc/self/exe", buffer, n); if (n == -1) - return -errno; + return UV__ERR(errno); buffer[n] = '\0'; *size = n; diff --git a/deps/uv/src/unix/proctitle.c b/deps/uv/src/unix/proctitle.c index 1b3a798820e282..1a8c7a7090e8a6 100644 --- a/deps/uv/src/unix/proctitle.c +++ b/deps/uv/src/unix/proctitle.c @@ -105,14 +105,14 @@ int uv_set_process_title(const char* title) { int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) - return -EINVAL; + return UV_EINVAL; uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); if (size <= process_title.len) { uv_mutex_unlock(&process_title_mutex); - return -ENOBUFS; + return UV_ENOBUFS; } if (process_title.len != 0) diff --git a/deps/uv/src/unix/signal.c b/deps/uv/src/unix/signal.c index 3759778011223f..b9d0a56084c6b3 100644 --- a/deps/uv/src/unix/signal.c +++ b/deps/uv/src/unix/signal.c @@ -225,7 +225,7 @@ static int uv__signal_register_handler(int signum, int oneshot) { /* XXX save old action so we can restore it later on? */ if (sigaction(signum, &sa, NULL)) - return -errno; + return UV__ERR(errno); return 0; } @@ -362,7 +362,7 @@ static int uv__signal_start(uv_signal_t* handle, * eventually. */ if (signum == 0) - return -EINVAL; + return UV_EINVAL; /* Short circuit: if the signal watcher is already watching {signum} don't * go through the process of deregistering and registering the handler. diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 6fc0a01f5a51a6..3e786abee015c4 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -58,6 +58,12 @@ struct uv__stream_select_s { fd_set* swrite; size_t swrite_sz; }; +# define WRITE_RETRY_ON_ERROR(send_handle) \ + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \ + (errno == EMSGSIZE && send_handle)) +#else +# define WRITE_RETRY_ON_ERROR(send_handle) \ + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) #endif /* defined(__APPLE__) */ static void uv__stream_connect(uv_stream_t*); @@ -282,7 +288,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { kq = kqueue(); if (kq == -1) { perror("(libuv) kqueue()"); - return -errno; + return UV__ERR(errno); } EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); @@ -298,7 +304,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { uv__close(kq); if (ret == -1) - return -errno; + return UV__ERR(errno); if (ret == 0 || (events[0].flags & EV_ERROR) == 0 || events[0].data != EINVAL) return 0; @@ -310,7 +316,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { * NOTE: do it ahead of malloc below to allocate enough space for fd_sets */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - return -errno; + return UV__ERR(errno); max_fd = *fd; if (fds[1] > max_fd) @@ -321,7 +327,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz); if (s == NULL) { - err = -ENOMEM; + err = UV_ENOMEM; goto failed_malloc; } @@ -395,18 +401,18 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { #endif if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd)) - return -EBUSY; + return UV_EBUSY; assert(fd >= 0); stream->flags |= flags; if (stream->type == UV_TCP) { if ((stream->flags & UV_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) - return -errno; + return UV__ERR(errno); /* TODO Use delay the user passed in. */ if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60)) - return -errno; + return UV__ERR(errno); } #if defined(__APPLE__) @@ -414,7 +420,7 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && errno != ENOTSOCK && errno != EINVAL) { - return -errno; + return UV__ERR(errno); } #endif @@ -445,11 +451,11 @@ void uv__stream_destroy(uv_stream_t* stream) { if (stream->connect_req) { uv__req_unregister(stream->loop, stream->connect_req); - stream->connect_req->cb(stream->connect_req, -ECANCELED); + stream->connect_req->cb(stream->connect_req, UV_ECANCELED); stream->connect_req = NULL; } - uv__stream_flush_write_queue(stream, -ECANCELED); + uv__stream_flush_write_queue(stream, UV_ECANCELED); uv__write_callbacks(stream); if (stream->shutdown_req) { @@ -459,7 +465,7 @@ void uv__stream_destroy(uv_stream_t* stream) { * callee that the handle has been destroyed. */ uv__req_unregister(stream->loop, stream->shutdown_req); - stream->shutdown_req->cb(stream->shutdown_req, -ECANCELED); + stream->shutdown_req->cb(stream->shutdown_req, UV_ECANCELED); stream->shutdown_req = NULL; } @@ -483,7 +489,7 @@ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { int emfile_fd; if (loop->emfile_fd == -1) - return -EMFILE; + return UV_EMFILE; uv__close(loop->emfile_fd); loop->emfile_fd = -1; @@ -492,7 +498,7 @@ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { err = uv__accept(accept_fd); if (err >= 0) uv__close(err); - } while (err >= 0 || err == -EINTR); + } while (err >= 0 || err == UV_EINTR); emfile_fd = uv__open_cloexec("/", O_RDONLY); if (emfile_fd >= 0) @@ -533,15 +539,15 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { err = uv__accept(uv__stream_fd(stream)); if (err < 0) { - if (err == -EAGAIN || err == -EWOULDBLOCK) + if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) return; /* Not an error. */ - if (err == -ECONNABORTED) + if (err == UV_ECONNABORTED) continue; /* Ignore. Nothing we can do about that. */ - if (err == -EMFILE || err == -ENFILE) { + if (err == UV_EMFILE || err == UV_ENFILE) { err = uv__emfile_trick(loop, uv__stream_fd(stream)); - if (err == -EAGAIN || err == -EWOULDBLOCK) + if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) break; } @@ -577,7 +583,7 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { assert(server->loop == client->loop); if (server->accepted_fd == -1) - return -EAGAIN; + return UV_EAGAIN; switch (client->type) { case UV_NAMED_PIPE: @@ -601,7 +607,7 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { break; default: - return -EINVAL; + return UV_EINVAL; } client->flags |= UV_HANDLE_BOUND; @@ -649,7 +655,7 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { break; default: - err = -EINVAL; + err = UV_EINVAL; } if (err == 0) @@ -680,7 +686,7 @@ static void uv__drain(uv_stream_t* stream) { err = 0; if (shutdown(uv__stream_fd(stream), SHUT_WR)) - err = -errno; + err = UV__ERR(errno); if (err == 0) stream->flags |= UV_STREAM_SHUT; @@ -792,7 +798,7 @@ static void uv__write(uv_stream_t* stream) { } scratch; if (uv__is_closing(req->send_handle)) { - err = -EBADF; + err = UV_EBADF; goto error; } @@ -859,8 +865,8 @@ static void uv__write(uv_stream_t* stream) { } if (n < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENOBUFS) { - err = -errno; + if (!WRITE_RETRY_ON_ERROR(req->send_handle)) { + err = UV__ERR(errno); goto error; } else if (stream->flags & UV_STREAM_BLOCKING) { /* If this is a blocking stream, try again. */ @@ -1029,7 +1035,7 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) + sizeof(*queued_fds)); if (queued_fds == NULL) - return -ENOMEM; + return UV_ENOMEM; queued_fds->size = queue_size; queued_fds->offset = 0; stream->queued_fds = queued_fds; @@ -1046,7 +1052,7 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { * NOTE: if it is fatal - sockets will be closed in uv__stream_close */ if (queued_fds == NULL) - return -ENOMEM; + return UV_ENOMEM; queued_fds->size = queue_size; stream->queued_fds = queued_fds; } @@ -1192,7 +1198,7 @@ static void uv__read(uv_stream_t* stream) { #endif } else { /* Error. User should call uv_close(). */ - stream->read_cb(stream, -errno, &buf); + stream->read_cb(stream, UV__ERR(errno), &buf); if (stream->flags & UV_STREAM_READING) { stream->flags &= ~UV_STREAM_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); @@ -1269,7 +1275,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { stream->flags & UV_STREAM_SHUT || stream->flags & UV_STREAM_SHUTTING || uv__is_closing(stream)) { - return -ENOTCONN; + return UV_ENOTCONN; } assert(uv__stream_fd(stream) >= 0); @@ -1368,10 +1374,10 @@ static void uv__stream_connect(uv_stream_t* stream) { SO_ERROR, &error, &errorsize); - error = -error; + error = UV__ERR(error); } - if (error == -EINPROGRESS) + if (error == UV__ERR(EINPROGRESS)) return; stream->connect_req = NULL; @@ -1388,7 +1394,7 @@ static void uv__stream_connect(uv_stream_t* stream) { return; if (error < 0) { - uv__stream_flush_write_queue(stream, -ECANCELED); + uv__stream_flush_write_queue(stream, UV_ECANCELED); uv__write_callbacks(stream); } } @@ -1409,11 +1415,11 @@ int uv_write2(uv_write_t* req, "uv_write (unix) does not yet support other types of streams"); if (uv__stream_fd(stream) < 0) - return -EBADF; + return UV_EBADF; if (send_handle) { if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) - return -EINVAL; + return UV_EINVAL; /* XXX We abuse uv_write2() to send over UDP handles to child processes. * Don't call uv__stream_fd() on those handles, it's a macro that on OS X @@ -1422,12 +1428,12 @@ int uv_write2(uv_write_t* req, * which works but only by accident. */ if (uv__handle_fd((uv_handle_t*) send_handle) < 0) - return -EBADF; + return UV_EBADF; #if defined(__CYGWIN__) || defined(__MSYS__) /* Cygwin recvmsg always sets msg_controllen to zero, so we cannot send it. See https://github.com/mirror/newlib-cygwin/blob/86fc4bf0/winsup/cygwin/fhandler_socket.cc#L1736-L1743 */ - return -ENOSYS; + return UV_ENOSYS; #endif } @@ -1452,7 +1458,7 @@ int uv_write2(uv_write_t* req, req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); if (req->bufs == NULL) - return -ENOMEM; + return UV_ENOMEM; memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); req->nbufs = nbufs; @@ -1516,7 +1522,7 @@ int uv_try_write(uv_stream_t* stream, /* Connecting or already writing some data */ if (stream->connect_req != NULL || stream->write_queue_size != 0) - return -EAGAIN; + return UV_EAGAIN; has_pollout = uv__io_active(&stream->io_watcher, POLLOUT); @@ -1547,7 +1553,7 @@ int uv_try_write(uv_stream_t* stream, } if (written == 0 && req_size != 0) - return -EAGAIN; + return UV_EAGAIN; else return written; } @@ -1560,7 +1566,7 @@ int uv_read_start(uv_stream_t* stream, stream->type == UV_TTY); if (stream->flags & UV_CLOSING) - return -EINVAL; + return UV_EINVAL; /* The UV_STREAM_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index a72c26a01f0a65..b6b3dfea77a8d7 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -73,7 +73,7 @@ int uv__platform_loop_init(uv_loop_t* loop) { fd = port_create(); if (fd == -1) - return -errno; + return UV__ERR(errno); err = uv__cloexec(fd, 1); if (err) { @@ -132,7 +132,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { int uv__io_check_fd(uv_loop_t* loop, int fd) { if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0)) - return -errno; + return UV__ERR(errno); if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) abort(); @@ -342,7 +342,7 @@ int uv_exepath(char* buffer, size_t* size) { char buf[128]; if (buffer == NULL || size == NULL || *size == 0) - return -EINVAL; + return UV_EINVAL; snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid()); @@ -351,7 +351,7 @@ int uv_exepath(char* buffer, size_t* size) { res = readlink(buf, buffer, res); if (res == -1) - return -errno; + return UV__ERR(errno); buffer[res] = '\0'; *size = res; @@ -378,14 +378,14 @@ void uv_loadavg(double avg[3]) { static int uv__fs_event_rearm(uv_fs_event_t *handle) { if (handle->fd == -1) - return -EBADF; + return UV_EBADF; if (port_associate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t) &handle->fo, FILE_ATTRIB | FILE_MODIFIED, handle) == -1) { - return -errno; + return UV__ERR(errno); } handle->fd = PORT_LOADED; @@ -462,13 +462,13 @@ int uv_fs_event_start(uv_fs_event_t* handle, int err; if (uv__is_active(handle)) - return -EINVAL; + return UV_EINVAL; first_run = 0; if (handle->loop->fs_fd == -1) { portfd = port_create(); if (portfd == -1) - return -errno; + return UV__ERR(errno); handle->loop->fs_fd = portfd; first_run = 1; } @@ -521,7 +521,7 @@ void uv__fs_event_close(uv_fs_event_t* handle) { #else /* !defined(PORT_SOURCE_FILE) */ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { - return -ENOSYS; + return UV_ENOSYS; } @@ -529,12 +529,12 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* filename, unsigned int flags) { - return -ENOSYS; + return UV_ENOSYS; } int uv_fs_event_stop(uv_fs_event_t* handle) { - return -ENOSYS; + return UV_ENOSYS; } @@ -552,10 +552,10 @@ int uv_resident_set_memory(size_t* rss) { fd = open("/proc/self/psinfo", O_RDONLY); if (fd == -1) - return -errno; + return UV__ERR(errno); /* FIXME(bnoordhuis) Handle EINTR. */ - err = -EINVAL; + err = UV_EINVAL; if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { *rss = (size_t)psinfo.pr_rssize * 1024; err = 0; @@ -575,7 +575,7 @@ int uv_uptime(double* uptime) { kc = kstat_open(); if (kc == NULL) - return -EPERM; + return UV_EPERM; ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc"); if (kstat_read(kc, ksp, NULL) == -1) { @@ -599,7 +599,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { kc = kstat_open(); if (kc == NULL) - return -EPERM; + return UV_EPERM; /* Get count of cpus */ lookup_instance = 0; @@ -610,7 +610,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { *cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos)); if (!(*cpu_infos)) { kstat_close(kc); - return -ENOMEM; + return UV_ENOMEM; } *count = lookup_instance; @@ -692,7 +692,7 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { #ifdef SUNOS_NO_IFADDRS int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { - return -ENOSYS; + return UV_ENOSYS; } #else /* SUNOS_NO_IFADDRS */ /* @@ -730,11 +730,11 @@ static int uv__set_phys_addr(uv_interface_address_t* address, sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) - return -errno; + return UV__ERR(errno); if (ioctl(sockfd, SIOCGARP, (char*)&arpreq) == -1) { uv__close(sockfd); - return -errno; + return UV__ERR(errno); } memcpy(address->phys_addr, arpreq.arp_ha.sa_data, sizeof(address->phys_addr)); uv__close(sockfd); @@ -759,7 +759,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs* ent; if (getifaddrs(&addrs)) - return -errno; + return UV__ERR(errno); *count = 0; @@ -773,7 +773,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { *addresses = uv__malloc(*count * sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); - return -ENOMEM; + return UV_ENOMEM; } address = *addresses; diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index c7c8d21c668080..96f89312def0eb 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -89,7 +89,7 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { slen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) - return -errno; + return UV__ERR(errno); if ((saddr.ss_family == AF_INET6 && ((struct sockaddr_in6*) &saddr)->sin6_port != 0) || @@ -102,7 +102,7 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { /* Bind to arbitrary port */ if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) - return -errno; + return UV__ERR(errno); } handle->flags |= flags; @@ -119,10 +119,10 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return -EINVAL; + return UV_EINVAL; if (flags & ~0xFF) - return -EINVAL; + return UV_EINVAL; uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); @@ -156,7 +156,7 @@ int uv__tcp_bind(uv_tcp_t* tcp, /* Cannot set IPv6-only mode on non-IPv6 socket. */ if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6) - return -EINVAL; + return UV_EINVAL; err = maybe_new_socket(tcp, addr->sa_family, @@ -166,7 +166,7 @@ int uv__tcp_bind(uv_tcp_t* tcp, on = 1; if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) - return -errno; + return UV__ERR(errno); #ifdef IPV6_V6ONLY if (addr->sa_family == AF_INET6) { @@ -178,9 +178,9 @@ int uv__tcp_bind(uv_tcp_t* tcp, sizeof on) == -1) { #if defined(__MVS__) if (errno == EOPNOTSUPP) - return -EINVAL; + return UV_EINVAL; #endif - return -errno; + return UV__ERR(errno); } } #endif @@ -190,10 +190,10 @@ int uv__tcp_bind(uv_tcp_t* tcp, if (errno == EAFNOSUPPORT) /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a * socket created with AF_INET to an AF_INET6 address or vice versa. */ - return -EINVAL; - return -errno; + return UV_EINVAL; + return UV__ERR(errno); } - tcp->delayed_error = -errno; + tcp->delayed_error = UV__ERR(errno); tcp->flags |= UV_HANDLE_BOUND; if (addr->sa_family == AF_INET6) @@ -214,7 +214,7 @@ int uv__tcp_connect(uv_connect_t* req, assert(handle->type == UV_TCP); if (handle->connect_req != NULL) - return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */ + return UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */ err = maybe_new_socket(handle, addr->sa_family, @@ -242,9 +242,9 @@ int uv__tcp_connect(uv_connect_t* req, * error. Solaris wants to report immediately--other unixes want to * wait. */ - handle->delayed_error = -errno; + handle->delayed_error = UV__ERR(errno); else - return -errno; + return UV__ERR(errno); } uv__req_init(handle->loop, req, UV_CONNECT); @@ -284,13 +284,13 @@ int uv_tcp_getsockname(const uv_tcp_t* handle, return handle->delayed_error; if (uv__stream_fd(handle) < 0) - return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ + return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getsockname(uv__stream_fd(handle), name, &socklen)) - return -errno; + return UV__ERR(errno); *namelen = (int) socklen; return 0; @@ -306,13 +306,13 @@ int uv_tcp_getpeername(const uv_tcp_t* handle, return handle->delayed_error; if (uv__stream_fd(handle) < 0) - return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ + return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getpeername(uv__stream_fd(handle), name, &socklen)) - return -errno; + return UV__ERR(errno); *namelen = (int) socklen; return 0; @@ -348,7 +348,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { return err; if (listen(tcp->io_watcher.fd, backlog)) - return -errno; + return UV__ERR(errno); tcp->connection_cb = cb; tcp->flags |= UV_HANDLE_BOUND; @@ -363,18 +363,18 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { int uv__tcp_nodelay(int fd, int on) { if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))) - return -errno; + return UV__ERR(errno); return 0; } int uv__tcp_keepalive(int fd, int on, unsigned int delay) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) - return -errno; + return UV__ERR(errno); #ifdef TCP_KEEPIDLE if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) - return -errno; + return UV__ERR(errno); #endif /* Solaris/SmartOS, if you don't support keep-alive, @@ -383,7 +383,7 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) { /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ #if defined(TCP_KEEPALIVE) && !defined(__sun) if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) - return -errno; + return UV__ERR(errno); #endif return 0; diff --git a/deps/uv/src/unix/thread.c b/deps/uv/src/unix/thread.c index abaca295d247c3..3def29457aafb6 100644 --- a/deps/uv/src/unix/thread.c +++ b/deps/uv/src/unix/thread.c @@ -200,7 +200,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { if (attr != NULL) pthread_attr_destroy(attr); - return -err; + return UV__ERR(err); } @@ -209,7 +209,7 @@ uv_thread_t uv_thread_self(void) { } int uv_thread_join(uv_thread_t *tid) { - return -pthread_join(*tid, NULL); + return UV__ERR(pthread_join(*tid, NULL)); } @@ -220,7 +220,7 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { int uv_mutex_init(uv_mutex_t* mutex) { #if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) - return -pthread_mutex_init(mutex, NULL); + return UV__ERR(pthread_mutex_init(mutex, NULL)); #else pthread_mutexattr_t attr; int err; @@ -236,7 +236,7 @@ int uv_mutex_init(uv_mutex_t* mutex) { if (pthread_mutexattr_destroy(&attr)) abort(); - return -err; + return UV__ERR(err); #endif } @@ -256,7 +256,7 @@ int uv_mutex_init_recursive(uv_mutex_t* mutex) { if (pthread_mutexattr_destroy(&attr)) abort(); - return -err; + return UV__ERR(err); } @@ -279,7 +279,7 @@ int uv_mutex_trylock(uv_mutex_t* mutex) { if (err) { if (err != EBUSY && err != EAGAIN) abort(); - return -EBUSY; + return UV_EBUSY; } return 0; @@ -293,7 +293,7 @@ void uv_mutex_unlock(uv_mutex_t* mutex) { int uv_rwlock_init(uv_rwlock_t* rwlock) { - return -pthread_rwlock_init(rwlock, NULL); + return UV__ERR(pthread_rwlock_init(rwlock, NULL)); } @@ -316,7 +316,7 @@ int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { if (err) { if (err != EBUSY && err != EAGAIN) abort(); - return -EBUSY; + return UV_EBUSY; } return 0; @@ -342,7 +342,7 @@ int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { if (err) { if (err != EBUSY && err != EAGAIN) abort(); - return -EBUSY; + return UV_EBUSY; } return 0; @@ -369,12 +369,12 @@ int uv_sem_init(uv_sem_t* sem, unsigned int value) { if (err == KERN_SUCCESS) return 0; if (err == KERN_INVALID_ARGUMENT) - return -EINVAL; + return UV_EINVAL; if (err == KERN_RESOURCE_SHORTAGE) - return -ENOMEM; + return UV_ENOMEM; abort(); - return -EINVAL; /* Satisfy the compiler. */ + return UV_EINVAL; /* Satisfy the compiler. */ } @@ -413,10 +413,10 @@ int uv_sem_trywait(uv_sem_t* sem) { if (err == KERN_SUCCESS) return 0; if (err == KERN_OPERATION_TIMED_OUT) - return -EAGAIN; + return UV_EAGAIN; abort(); - return -EINVAL; /* Satisfy the compiler. */ + return UV_EINVAL; /* Satisfy the compiler. */ } #elif defined(__MVS__) @@ -433,14 +433,14 @@ int uv_sem_init(uv_sem_t* sem, unsigned int value) { semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR); if (semid == -1) - return -errno; + return UV__ERR(errno); arg.val = value; if (-1 == semctl(semid, 0, SETVAL, arg)) { err = errno; if (-1 == semctl(*sem, 0, IPC_RMID)) abort(); - return -err; + return UV__ERR(err); } *sem = semid; @@ -493,7 +493,7 @@ int uv_sem_trywait(uv_sem_t* sem) { if (op_status) { if (errno == EAGAIN) - return -EAGAIN; + return UV_EAGAIN; abort(); } @@ -504,7 +504,7 @@ int uv_sem_trywait(uv_sem_t* sem) { int uv_sem_init(uv_sem_t* sem, unsigned int value) { if (sem_init(sem, 0, value)) - return -errno; + return UV__ERR(errno); return 0; } @@ -542,7 +542,7 @@ int uv_sem_trywait(uv_sem_t* sem) { if (r) { if (errno == EAGAIN) - return -EAGAIN; + return UV_EAGAIN; abort(); } @@ -555,7 +555,7 @@ int uv_sem_trywait(uv_sem_t* sem) { #if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__) int uv_cond_init(uv_cond_t* cond) { - return -pthread_cond_init(cond, NULL); + return UV__ERR(pthread_cond_init(cond, NULL)); } #else /* !(defined(__APPLE__) && defined(__MACH__)) */ @@ -566,7 +566,7 @@ int uv_cond_init(uv_cond_t* cond) { err = pthread_condattr_init(&attr); if (err) - return -err; + return UV__ERR(err); #if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); @@ -588,7 +588,7 @@ int uv_cond_init(uv_cond_t* cond) { pthread_cond_destroy(cond); error2: pthread_condattr_destroy(&attr); - return -err; + return UV__ERR(err); } #endif /* defined(__APPLE__) && defined(__MACH__) */ @@ -646,13 +646,22 @@ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { int r; struct timespec ts; +#if defined(__MVS__) + struct timeval tv; +#endif #if defined(__APPLE__) && defined(__MACH__) ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); +#else +#if defined(__MVS__) + if (gettimeofday(&tv, NULL)) + abort(); + timeout += tv.tv_sec * NANOSEC + tv.tv_usec * 1e3; #else timeout += uv__hrtime(UV_CLOCK_PRECISE); +#endif ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 @@ -672,15 +681,15 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { return 0; if (r == ETIMEDOUT) - return -ETIMEDOUT; + return UV_ETIMEDOUT; abort(); - return -EINVAL; /* Satisfy the compiler. */ + return UV_EINVAL; /* Satisfy the compiler. */ } int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - return -pthread_barrier_init(barrier, NULL, count); + return UV__ERR(pthread_barrier_init(barrier, NULL, count)); } @@ -699,7 +708,7 @@ int uv_barrier_wait(uv_barrier_t* barrier) { int uv_key_create(uv_key_t* key) { - return -pthread_key_create(key, NULL); + return UV__ERR(pthread_key_create(key, NULL)); } diff --git a/deps/uv/src/unix/timer.c b/deps/uv/src/unix/timer.c index f46bdf4bf52e61..54dabfe7df9e27 100644 --- a/deps/uv/src/unix/timer.c +++ b/deps/uv/src/unix/timer.c @@ -66,7 +66,7 @@ int uv_timer_start(uv_timer_t* handle, uint64_t clamped_timeout; if (cb == NULL) - return -EINVAL; + return UV_EINVAL; if (uv__is_active(handle)) uv_timer_stop(handle); @@ -105,7 +105,7 @@ int uv_timer_stop(uv_timer_t* handle) { int uv_timer_again(uv_timer_t* handle) { if (handle->timer_cb == NULL) - return -EINVAL; + return UV_EINVAL; if (handle->repeat) { uv_timer_stop(handle); diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index 357f9748f65bf1..f22b3b80de061f 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -106,7 +106,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { */ type = uv_guess_handle(fd); if (type == UV_FILE || type == UV_UNKNOWN_HANDLE) - return -EINVAL; + return UV_EINVAL; flags = 0; newfd = -1; @@ -142,7 +142,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { newfd = r; r = uv__dup2_cloexec(newfd, fd); - if (r < 0 && r != -EINVAL) { + if (r < 0 && r != UV_EINVAL) { /* EINVAL means newfd == fd which could conceivably happen if another * thread called close(fd) between our calls to isatty() and open(). * That's a rather unlikely event but let's handle it anyway. @@ -163,7 +163,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { if (saved_flags == -1) { if (newfd != -1) uv__close(newfd); - return -errno; + return UV__ERR(errno); } #endif @@ -234,7 +234,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { fd = uv__stream_fd(tty); if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { if (tcgetattr(fd, &tty->orig_termios)) - return -errno; + return UV__ERR(errno); /* This is used for uv_tty_reset_mode() */ uv_spinlock_lock(&termios_spinlock); @@ -264,7 +264,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { /* Apply changes after draining */ if (tcsetattr(fd, TCSADRAIN, &tmp)) - return -errno; + return UV__ERR(errno); tty->mode = mode; return 0; @@ -280,7 +280,7 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { while (err == -1 && errno == EINTR); if (err == -1) - return -errno; + return UV__ERR(errno); *width = ws.ws_col; *height = ws.ws_row; @@ -358,12 +358,12 @@ int uv_tty_reset_mode(void) { saved_errno = errno; if (!uv_spinlock_trylock(&termios_spinlock)) - return -EBUSY; /* In uv_tty_set_mode(). */ + return UV_EBUSY; /* In uv_tty_set_mode(). */ err = 0; if (orig_termios_fd != -1) if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios)) - err = -errno; + err = UV__ERR(errno); uv_spinlock_unlock(&termios_spinlock); errno = saved_errno; diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index a475bf5741634d..74d613b6843b7d 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -72,7 +72,7 @@ void uv__udp_finish_close(uv_udp_t* handle) { QUEUE_REMOVE(q); req = QUEUE_DATA(q, uv_udp_send_t, queue); - req->status = -ECANCELED; + req->status = UV_ECANCELED; QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); } @@ -189,7 +189,7 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { if (errno == EAGAIN || errno == EWOULDBLOCK) handle->recv_cb(handle, 0, &buf, NULL, 0); else - handle->recv_cb(handle, -errno, &buf, NULL, 0); + handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0); } else { const struct sockaddr *addr; @@ -242,7 +242,7 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { break; } - req->status = (size == -1 ? -errno : size); + req->status = (size == -1 ? UV__ERR(errno) : size); /* Sending a datagram is an atomic operation: either all data * is written or nothing is (and EMSGSIZE is raised). That is @@ -270,11 +270,11 @@ static int uv__set_reuse(int fd) { #if defined(SO_REUSEPORT) && !defined(__linux__) yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) - return -errno; + return UV__ERR(errno); #else yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) - return -errno; + return UV__ERR(errno); #endif return 0; @@ -291,11 +291,11 @@ int uv__udp_bind(uv_udp_t* handle, /* Check for bad flags. */ if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) - return -EINVAL; + return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) - return -EINVAL; + return UV_EINVAL; fd = handle->io_watcher.fd; if (fd == -1) { @@ -316,21 +316,21 @@ int uv__udp_bind(uv_udp_t* handle, #ifdef IPV6_V6ONLY yes = 1; if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) { - err = -errno; + err = UV__ERR(errno); return err; } #else - err = -ENOTSUP; + err = UV_ENOTSUP; return err; #endif } if (bind(fd, addr, addrlen)) { - err = -errno; + err = UV__ERR(errno); if (errno == EAFNOSUPPORT) /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a * socket created with AF_INET to an AF_INET6 address or vice versa. */ - err = -EINVAL; + err = UV_EINVAL; return err; } @@ -418,7 +418,7 @@ int uv__udp_send(uv_udp_send_t* req, if (req->bufs == NULL) { uv__req_unregister(handle->loop, req); - return -ENOMEM; + return UV_ENOMEM; } memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); @@ -457,7 +457,7 @@ int uv__udp_try_send(uv_udp_t* handle, /* already sending a message */ if (handle->send_queue_count != 0) - return -EAGAIN; + return UV_EAGAIN; err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); if (err) @@ -475,9 +475,9 @@ int uv__udp_try_send(uv_udp_t* handle, if (size == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return -EAGAIN; + return UV_EAGAIN; else - return -errno; + return UV__ERR(errno); } return size; @@ -512,7 +512,7 @@ static int uv__udp_set_membership4(uv_udp_t* handle, optname = IP_DROP_MEMBERSHIP; break; default: - return -EINVAL; + return UV_EINVAL; } if (setsockopt(handle->io_watcher.fd, @@ -522,9 +522,9 @@ static int uv__udp_set_membership4(uv_udp_t* handle, sizeof(mreq))) { #if defined(__MVS__) if (errno == ENXIO) - return -ENODEV; + return UV_ENODEV; #endif - return -errno; + return UV__ERR(errno); } return 0; @@ -543,7 +543,7 @@ static int uv__udp_set_membership6(uv_udp_t* handle, if (interface_addr) { if (uv_ip6_addr(interface_addr, 0, &addr6)) - return -EINVAL; + return UV_EINVAL; mreq.ipv6mr_interface = addr6.sin6_scope_id; } else { mreq.ipv6mr_interface = 0; @@ -559,7 +559,7 @@ static int uv__udp_set_membership6(uv_udp_t* handle, optname = IPV6_DROP_MEMBERSHIP; break; default: - return -EINVAL; + return UV_EINVAL; } if (setsockopt(handle->io_watcher.fd, @@ -569,9 +569,9 @@ static int uv__udp_set_membership6(uv_udp_t* handle, sizeof(mreq))) { #if defined(__MVS__) if (errno == ENXIO) - return -ENODEV; + return UV_ENODEV; #endif - return -errno; + return UV__ERR(errno); } return 0; @@ -586,10 +586,10 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return -EINVAL; + return UV_EINVAL; if (flags & ~0xFF) - return -EINVAL; + return UV_EINVAL; if (domain != AF_UNSPEC) { err = uv__socket(domain, SOCK_DGRAM, 0); @@ -622,7 +622,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { /* Check for already active socket. */ if (handle->io_watcher.fd != -1) - return -EBUSY; + return UV_EBUSY; err = uv__nonblock(sock, 1); if (err) @@ -656,7 +656,7 @@ int uv_udp_set_membership(uv_udp_t* handle, return err; return uv__udp_set_membership6(handle, &addr6, interface_addr, membership); } else { - return -EINVAL; + return UV_EINVAL; } } @@ -680,7 +680,7 @@ static int uv__setsockopt(uv_udp_t* handle, val, size); if (r) - return -errno; + return UV__ERR(errno); return 0; } @@ -698,7 +698,7 @@ static int uv__setsockopt_maybe_char(uv_udp_t* handle, #endif if (val < 0 || val > 255) - return -EINVAL; + return UV_EINVAL; return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg)); } @@ -710,7 +710,7 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int on) { SO_BROADCAST, &on, sizeof(on))) { - return -errno; + return UV__ERR(errno); } return 0; @@ -719,11 +719,11 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int on) { int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { if (ttl < 1 || ttl > 255) - return -EINVAL; + return UV_EINVAL; #if defined(__MVS__) if (!(handle->flags & UV_HANDLE_IPV6)) - return -ENOTSUP; /* zOS does not support setting ttl for IPv4 */ + return UV_ENOTSUP; /* zOS does not support setting ttl for IPv4 */ #endif /* @@ -817,7 +817,7 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { /* nothing, address was parsed */ } else { - return -EINVAL; + return UV_EINVAL; } if (addr_st.ss_family == AF_INET) { @@ -826,7 +826,7 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) IP_MULTICAST_IF, (void*) &addr4->sin_addr, sizeof(addr4->sin_addr)) == -1) { - return -errno; + return UV__ERR(errno); } } else if (addr_st.ss_family == AF_INET6) { if (setsockopt(handle->io_watcher.fd, @@ -834,7 +834,7 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) IPV6_MULTICAST_IF, &addr6->sin6_scope_id, sizeof(addr6->sin6_scope_id)) == -1) { - return -errno; + return UV__ERR(errno); } } else { assert(0 && "unexpected address family"); @@ -851,13 +851,13 @@ int uv_udp_getsockname(const uv_udp_t* handle, socklen_t socklen; if (handle->io_watcher.fd == -1) - return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ + return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ /* sizeof(socklen_t) != sizeof(int) on some systems. */ socklen = (socklen_t) *namelen; if (getsockname(handle->io_watcher.fd, name, &socklen)) - return -errno; + return UV__ERR(errno); *namelen = (int) socklen; return 0; @@ -870,10 +870,10 @@ int uv__udp_recv_start(uv_udp_t* handle, int err; if (alloc_cb == NULL || recv_cb == NULL) - return -EINVAL; + return UV_EINVAL; if (uv__io_active(&handle->io_watcher, POLLIN)) - return -EALREADY; /* FIXME(bnoordhuis) Should be -EBUSY. */ + return UV_EALREADY; /* FIXME(bnoordhuis) Should be UV_EBUSY. */ err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0); if (err) diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 781a8559dc8428..d4fa22aaef6add 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -41,6 +41,12 @@ #include "tree.h" #include "queue.h" +#if EDOM > 0 +# define UV__ERR(x) (-(x)) +#else +# define UV__ERR(x) (x) +#endif + #if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 extern int snprintf(char*, size_t, const char*, ...); #endif diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 097b00e08d50d5..6e0bdc7bb20e66 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -245,6 +245,7 @@ INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, req->ptr = NULL; req->path = NULL; req->cb = cb; + req->fs.info.bufs = NULL; memset(&req->fs, 0, sizeof(req->fs)); } diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index cc06d9e22abcb6..7523522217392e 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -1061,11 +1061,16 @@ int uv_spawn(uv_loop_t* loop, process_flags = CREATE_UNICODE_ENVIRONMENT; if (options->flags & UV_PROCESS_WINDOWS_HIDE) { + /* Avoid creating console window if stdio is not inherited. */ + for (i = 0; i < options->stdio_count; i++) { + if (options->stdio[i].flags & UV_INHERIT_FD) + break; + if (i == options->stdio_count - 1) + process_flags |= CREATE_NO_WINDOW; + } + /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; - - /* Hide console windows. */ - process_flags |= CREATE_NO_WINDOW; } else { startup.wShowWindow = SW_SHOWDEFAULT; } diff --git a/deps/uv/src/win/winsock.c b/deps/uv/src/win/winsock.c index 7cfa90f8af5127..84188954d815f1 100644 --- a/deps/uv/src/win/winsock.c +++ b/deps/uv/src/win/winsock.c @@ -580,8 +580,10 @@ int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, memcpy(dest6, addr, sizeof(*dest6)); if (memcmp(&dest6->sin6_addr, &uv_addr_ip6_any_.sin6_addr, - sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) - dest6->sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; + sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) { + struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT; + dest6->sin6_addr = init_sin6_addr; + } return 0; default: return UV_EINVAL; diff --git a/deps/uv/test/test-condvar.c b/deps/uv/test/test-condvar.c index 83b28494adbb3f..d956efef3c5a00 100644 --- a/deps/uv/test/test-condvar.c +++ b/deps/uv/test/test-condvar.c @@ -34,7 +34,7 @@ typedef struct worker_config { volatile int posted_1; volatile int posted_2; void (*signal_cond)(struct worker_config* c, volatile int* flag); - void (*wait_cond)(struct worker_config* c, const volatile int* flag); + int (*wait_cond)(struct worker_config* c, const volatile int* flag); } worker_config; @@ -44,6 +44,9 @@ static void worker(void* arg) { c->wait_cond(c, &c->posted_2); } +static void noop_worker(void* arg) { + return; +} static void condvar_signal(worker_config* c, volatile int* flag) { if (c->signal_delay) @@ -60,7 +63,7 @@ static void condvar_signal(worker_config* c, volatile int* flag) { } -static void condvar_wait(worker_config* c, const volatile int* flag) { +static int condvar_wait(worker_config* c, const volatile int* flag) { uv_mutex_lock(&c->mutex); if (c->wait_delay) uv_sleep(c->wait_delay); @@ -69,6 +72,8 @@ static void condvar_wait(worker_config* c, const volatile int* flag) { } ASSERT(*flag == 1); uv_mutex_unlock(&c->mutex); + + return 0; } @@ -85,7 +90,7 @@ TEST_IMPL(condvar_1) { ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); - wc.wait_cond(&wc, &wc.posted_1); + ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); @@ -109,7 +114,7 @@ TEST_IMPL(condvar_2) { ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); - wc.wait_cond(&wc, &wc.posted_1); + ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); @@ -120,20 +125,26 @@ TEST_IMPL(condvar_2) { } -static void condvar_timedwait(worker_config* c, const volatile int* flag) { +static int condvar_timedwait(worker_config* c, const volatile int* flag) { int r; + r = 0; + uv_mutex_lock(&c->mutex); if (c->wait_delay) uv_sleep(c->wait_delay); while (*flag == 0) { r = uv_cond_timedwait(&c->cond, &c->mutex, (uint64_t)(150 * 1e6)); - ASSERT(r == 0); + ASSERT(r == 0 || r == UV_ETIMEDOUT); + if (r == UV_ETIMEDOUT) + break; } uv_mutex_unlock(&c->mutex); -} + return r; +} +/* Test that uv_cond_timedwait will return early when cond is signaled. */ TEST_IMPL(condvar_3) { uv_thread_t thread; worker_config wc; @@ -147,7 +158,7 @@ TEST_IMPL(condvar_3) { ASSERT(0 == uv_mutex_init(&wc.mutex)); ASSERT(0 == uv_thread_create(&thread, worker, &wc)); - wc.wait_cond(&wc, &wc.posted_1); + ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); ASSERT(0 == uv_thread_join(&thread)); @@ -205,3 +216,30 @@ TEST_IMPL(condvar_5) { return 0; } + +/* Test that uv_cond_timedwait will time out when cond is not signaled. */ +TEST_IMPL(condvar_6) { + uv_thread_t thread; + worker_config wc; + int r; + + memset(&wc, 0, sizeof(wc)); + wc.signal_delay = 100; + wc.signal_cond = condvar_signal; + wc.wait_cond = condvar_timedwait; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, noop_worker, &wc)); + + /* This can only return having timed out, because otherwise we + * loop forever in condvar_timedwait. */ + r = wc.wait_cond(&wc, &wc.posted_1); + ASSERT(r == UV_ETIMEDOUT); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} diff --git a/deps/uv/test/test-fork.c b/deps/uv/test/test-fork.c index 924c65b2141134..39b59c8f20ebb4 100644 --- a/deps/uv/test/test-fork.c +++ b/deps/uv/test/test-fork.c @@ -335,7 +335,7 @@ TEST_IMPL(fork_signal_to_child_closed) { /* Note that we're deliberately not running the loop * in the child, and also not closing the loop's handles, * so the child default loop can't be cleanly closed. - * We need te explicitly exit to avoid an automatic failure + * We need to explicitly exit to avoid an automatic failure * in that case. */ exit(0); diff --git a/deps/uv/test/test-fs-copyfile.c b/deps/uv/test/test-fs-copyfile.c index 460c1dc6ae0b9f..4b1fdc5e798280 100644 --- a/deps/uv/test/test-fs-copyfile.c +++ b/deps/uv/test/test-fs-copyfile.c @@ -36,6 +36,10 @@ static const char dst[] = "test_file_dst"; static int result_check_count; +static void fail_cb(uv_fs_t* req) { + FATAL("fail_cb should not have been called"); +} + static void handle_result(uv_fs_t* req) { uv_fs_t stat_req; uint64_t size; @@ -158,7 +162,12 @@ TEST_IMPL(fs_copyfile) { ASSERT(result_check_count == 5); uv_run(loop, UV_RUN_DEFAULT); ASSERT(result_check_count == 6); - unlink(dst); /* Cleanup */ + /* If the flags are invalid, the loop should not be kept open */ + unlink(dst); + r = uv_fs_copyfile(loop, &req, fixture, dst, -1, fail_cb); + ASSERT(r == UV_EINVAL); + uv_run(loop, UV_RUN_DEFAULT); + unlink(dst); /* Cleanup */ return 0; } diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c index dc47b3a62d2399..39d73300dc2b61 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -199,7 +199,7 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle, fs_event_cb_called++; ASSERT(handle == &fs_event); ASSERT(status == 0); - ASSERT(events == UV_CHANGE || UV_RENAME); + ASSERT(events == UV_CHANGE || events == UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); #else @@ -283,7 +283,7 @@ static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle, fs_event_cb_called++; ASSERT(handle == &fs_event); ASSERT(status == 0); - ASSERT(events == UV_CHANGE || UV_RENAME); + ASSERT(events == UV_CHANGE || events == UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) ASSERT(strncmp(filename, file_prefix_in_subdir, diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 7c481f0711978f..3318b86649de50 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -319,6 +319,9 @@ static void ftruncate_cb(uv_fs_t* req) { ASSERT(r == 0); } +static void fail_cb(uv_fs_t* req) { + FATAL("fail_cb should not have been called"); +} static void read_cb(uv_fs_t* req) { int r; @@ -2884,7 +2887,19 @@ TEST_IMPL(fs_read_write_null_arguments) { uv_fs_req_cleanup(&read_req); r = uv_fs_write(NULL, &write_req, 0, NULL, 0, -1, NULL); + /* Validate some memory management on failed input validation before sending + fs work to the thread pool. */ ASSERT(r == UV_EINVAL); + ASSERT(write_req.path == NULL); + ASSERT(write_req.ptr == NULL); +#ifdef _WIN32 + ASSERT(write_req.file.pathw == NULL); + ASSERT(write_req.fs.info.new_pathw == NULL); + ASSERT(write_req.fs.info.bufs == NULL); +#else + ASSERT(write_req.new_path == NULL); + ASSERT(write_req.bufs == NULL); +#endif uv_fs_req_cleanup(&write_req); iov = uv_buf_init(NULL, 0); @@ -2897,6 +2912,31 @@ TEST_IMPL(fs_read_write_null_arguments) { ASSERT(r == UV_EINVAL); uv_fs_req_cleanup(&write_req); + /* If the arguments are invalid, the loop should not be kept open */ + loop = uv_default_loop(); + + r = uv_fs_read(loop, &read_req, 0, NULL, 0, -1, fail_cb); + ASSERT(r == UV_EINVAL); + uv_run(loop, UV_RUN_DEFAULT); + uv_fs_req_cleanup(&read_req); + + r = uv_fs_write(loop, &write_req, 0, NULL, 0, -1, fail_cb); + ASSERT(r == UV_EINVAL); + uv_run(loop, UV_RUN_DEFAULT); + uv_fs_req_cleanup(&write_req); + + iov = uv_buf_init(NULL, 0); + r = uv_fs_read(loop, &read_req, 0, &iov, 0, -1, fail_cb); + ASSERT(r == UV_EINVAL); + uv_run(loop, UV_RUN_DEFAULT); + uv_fs_req_cleanup(&read_req); + + iov = uv_buf_init(NULL, 0); + r = uv_fs_write(loop, &write_req, 0, &iov, 0, -1, fail_cb); + ASSERT(r == UV_EINVAL); + uv_run(loop, UV_RUN_DEFAULT); + uv_fs_req_cleanup(&write_req); + return 0; } @@ -3084,7 +3124,7 @@ TEST_IMPL(fs_exclusive_sharing_mode) { unlink("test_file"); ASSERT(UV_FS_O_EXLOCK > 0); - + r = uv_fs_open(NULL, &open_req1, "test_file", diff --git a/deps/uv/test/test-ipc-send-recv.c b/deps/uv/test/test-ipc-send-recv.c index 160c235078b939..917744cbaed978 100644 --- a/deps/uv/test/test-ipc-send-recv.c +++ b/deps/uv/test/test-ipc-send-recv.c @@ -304,7 +304,7 @@ static void read_cb(uv_stream_t* handle, union handles* recv; uv_write_t* write_req; - if (nread == UV__EOF || nread == UV__ECONNABORTED) { + if (nread == UV_EOF || nread == UV_ECONNABORTED) { return; } diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 5a50ec6713f03f..ff0a31d16bb940 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -42,6 +42,7 @@ TEST_DECLARE (condvar_2) TEST_DECLARE (condvar_3) TEST_DECLARE (condvar_4) TEST_DECLARE (condvar_5) +TEST_DECLARE (condvar_6) TEST_DECLARE (semaphore_1) TEST_DECLARE (semaphore_2) TEST_DECLARE (semaphore_3) @@ -445,6 +446,7 @@ TASK_LIST_START TEST_ENTRY (condvar_3) TEST_ENTRY (condvar_4) TEST_ENTRY (condvar_5) + TEST_ENTRY (condvar_6) TEST_ENTRY (semaphore_1) TEST_ENTRY (semaphore_2) TEST_ENTRY (semaphore_3) diff --git a/deps/uv/test/test.gyp b/deps/uv/test/test.gyp new file mode 100644 index 00000000000000..480e5a26c4176d --- /dev/null +++ b/deps/uv/test/test.gyp @@ -0,0 +1,279 @@ +{ + 'targets': [ + { + 'target_name': 'run-tests', + 'type': 'executable', + 'dependencies': [ '../uv.gyp:libuv' ], + 'sources': [ + 'blackhole-server.c', + 'echo-server.c', + 'run-tests.c', + 'runner.c', + 'runner.h', + 'test-get-loadavg.c', + 'task.h', + 'test-active.c', + 'test-async.c', + 'test-async-null-cb.c', + 'test-callback-stack.c', + 'test-callback-order.c', + 'test-close-fd.c', + 'test-close-order.c', + 'test-connect-unspecified.c', + 'test-connection-fail.c', + 'test-cwd-and-chdir.c', + 'test-default-loop-close.c', + 'test-delayed-accept.c', + 'test-eintr-handling.c', + 'test-error.c', + 'test-embed.c', + 'test-emfile.c', + 'test-env-vars.c', + 'test-fail-always.c', + 'test-fork.c', + 'test-fs.c', + 'test-fs-copyfile.c', + 'test-fs-event.c', + 'test-getters-setters.c', + 'test-get-currentexe.c', + 'test-get-memory.c', + 'test-get-passwd.c', + 'test-getaddrinfo.c', + 'test-gethostname.c', + 'test-getnameinfo.c', + 'test-getsockname.c', + 'test-handle-fileno.c', + 'test-homedir.c', + 'test-hrtime.c', + 'test-idle.c', + 'test-ip6-addr.c', + 'test-ipc.c', + 'test-ipc-send-recv.c', + 'test-list.h', + 'test-loop-handles.c', + 'test-loop-alive.c', + 'test-loop-close.c', + 'test-loop-stop.c', + 'test-loop-time.c', + 'test-loop-configure.c', + 'test-walk-handles.c', + 'test-watcher-cross-stop.c', + 'test-multiple-listen.c', + 'test-osx-select.c', + 'test-pass-always.c', + 'test-ping-pong.c', + 'test-pipe-bind-error.c', + 'test-pipe-connect-error.c', + 'test-pipe-connect-multiple.c', + 'test-pipe-connect-prepare.c', + 'test-pipe-getsockname.c', + 'test-pipe-pending-instances.c', + 'test-pipe-sendmsg.c', + 'test-pipe-server-close.c', + 'test-pipe-close-stdout-read-stdin.c', + 'test-pipe-set-non-blocking.c', + 'test-pipe-set-fchmod.c', + 'test-platform-output.c', + 'test-poll.c', + 'test-poll-close.c', + 'test-poll-close-doesnt-corrupt-stack.c', + 'test-poll-closesocket.c', + 'test-poll-oob.c', + 'test-process-title.c', + 'test-process-title-threadsafe.c', + 'test-queue-foreach-delete.c', + 'test-ref.c', + 'test-run-nowait.c', + 'test-run-once.c', + 'test-semaphore.c', + 'test-shutdown-close.c', + 'test-shutdown-eof.c', + 'test-shutdown-twice.c', + 'test-signal.c', + 'test-signal-multiple-loops.c', + 'test-socket-buffer-size.c', + 'test-spawn.c', + 'test-fs-poll.c', + 'test-stdio-over-pipes.c', + 'test-tcp-alloc-cb-fail.c', + 'test-tcp-bind-error.c', + 'test-tcp-bind6-error.c', + 'test-tcp-close.c', + 'test-tcp-close-accept.c', + 'test-tcp-close-while-connecting.c', + 'test-tcp-create-socket-early.c', + 'test-tcp-connect-error-after-write.c', + 'test-tcp-shutdown-after-write.c', + 'test-tcp-flags.c', + 'test-tcp-connect-error.c', + 'test-tcp-connect-timeout.c', + 'test-tcp-connect6-error.c', + 'test-tcp-open.c', + 'test-tcp-write-to-half-open-connection.c', + 'test-tcp-write-after-connect.c', + 'test-tcp-writealot.c', + 'test-tcp-write-fail.c', + 'test-tcp-try-write.c', + 'test-tcp-unexpected-read.c', + 'test-tcp-oob.c', + 'test-tcp-read-stop.c', + 'test-tcp-write-queue-order.c', + 'test-threadpool.c', + 'test-threadpool-cancel.c', + 'test-thread-equal.c', + 'test-tmpdir.c', + 'test-mutexes.c', + 'test-thread.c', + 'test-barrier.c', + 'test-condvar.c', + 'test-timer-again.c', + 'test-timer-from-check.c', + 'test-timer.c', + 'test-tty.c', + 'test-udp-alloc-cb-fail.c', + 'test-udp-bind.c', + 'test-udp-create-socket-early.c', + 'test-udp-dgram-too-big.c', + 'test-udp-ipv6.c', + 'test-udp-open.c', + 'test-udp-options.c', + 'test-udp-send-and-recv.c', + 'test-udp-send-hang-loop.c', + 'test-udp-send-immediate.c', + 'test-udp-send-unreachable.c', + 'test-udp-multicast-join.c', + 'test-udp-multicast-join6.c', + 'test-dlerror.c', + 'test-udp-multicast-ttl.c', + 'test-ip4-addr.c', + 'test-ip6-addr.c', + 'test-udp-multicast-interface.c', + 'test-udp-multicast-interface6.c', + 'test-udp-try-send.c', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'runner-win.c', + 'runner-win.h', + '../src/win/snprintf.c', + ], + 'libraries': [ '-lws2_32' ] + }, { # POSIX + 'sources': [ + 'runner-unix.c', + 'runner-unix.h', + ], + 'conditions': [ + [ 'OS != "zos"', { + 'defines': [ '_GNU_SOURCE' ], + 'cflags': [ '-Wno-long-long' ], + 'xcode_settings': { + 'WARNING_CFLAGS': [ '-Wno-long-long' ] + } + }], + ]}, + ], + [ 'OS in "mac dragonflybsd freebsd linux netbsd openbsd".split()', { + 'link_settings': { + 'libraries': [ '-lutil' ], + }, + }], + [ 'OS=="solaris"', { # make test-fs.c compile, needs _POSIX_C_SOURCE + 'defines': [ + '__EXTENSIONS__', + '_XOPEN_SOURCE=500', + ], + }], + [ 'OS=="aix"', { # make test-fs.c compile, needs _POSIX_C_SOURCE + 'defines': [ + '_ALL_SOURCE', + '_XOPEN_SOURCE=500', + ], + }], + [ 'OS == "zos"', { + 'cflags': [ '-qxplink' ], + 'ldflags': [ '-qxplink' ], + }], + ['uv_library=="shared_library"', { + 'defines': [ 'USING_UV_SHARED=1' ], + 'conditions': [ + [ 'OS == "zos"', { + 'cflags': [ '-Wc,DLL' ], + }], + ], + }], + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + }, + + { + 'target_name': 'run-benchmarks', + 'type': 'executable', + 'dependencies': [ '../uv.gyp:libuv' ], + 'sources': [ + 'benchmark-async.c', + 'benchmark-async-pummel.c', + 'benchmark-fs-stat.c', + 'benchmark-getaddrinfo.c', + 'benchmark-list.h', + 'benchmark-loop-count.c', + 'benchmark-million-async.c', + 'benchmark-million-timers.c', + 'benchmark-multi-accept.c', + 'benchmark-ping-pongs.c', + 'benchmark-pound.c', + 'benchmark-pump.c', + 'benchmark-sizes.c', + 'benchmark-spawn.c', + 'benchmark-thread.c', + 'benchmark-tcp-write-batch.c', + 'benchmark-udp-pummel.c', + 'dns-server.c', + 'echo-server.c', + 'blackhole-server.c', + 'run-benchmarks.c', + 'runner.c', + 'runner.h', + 'task.h', + ], + 'conditions': [ + [ 'OS=="win"', { + 'sources': [ + 'runner-win.c', + 'runner-win.h', + '../src/win/snprintf.c', + ], + 'libraries': [ '-lws2_32' ] + }, { # POSIX + 'defines': [ '_GNU_SOURCE' ], + 'sources': [ + 'runner-unix.c', + 'runner-unix.h', + ] + }], + [ 'OS == "zos"', { + 'cflags': [ '-qxplink' ], + 'ldflags': [ '-qxplink' ], + }], + ['uv_library=="shared_library"', { + 'defines': [ 'USING_UV_SHARED=1' ], + 'conditions': [ + [ 'OS == "zos"', { + 'cflags': [ '-Wc,DLL' ], + }], + ], + }], + ], + 'msvs-settings': { + 'VCLinkerTool': { + 'SubSystem': 1, # /subsystem:console + }, + }, + }, + ], +} diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 46606c5bda868d..a5046b87ea5b3c 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -1,44 +1,38 @@ { - 'target_defaults': { + 'variables': { 'conditions': [ - ['OS != "win"', { - 'defines': [ + ['OS=="win"', { + 'shared_unix_defines': [ '_LARGEFILE_SOURCE', '_FILE_OFFSET_BITS=64', ], - 'conditions': [ - ['OS=="solaris"', { - 'cflags': [ '-pthreads' ], - }], - ['OS not in "solaris android zos"', { - 'cflags': [ '-pthread' ], - }], - ['OS in "zos"', { - 'defines': [ - '_UNIX03_THREADS', - '_UNIX03_SOURCE', - '_UNIX03_WITHDRAWN', - '_OPEN_SYS_IF_EXT', - '_OPEN_SYS_SOCK_IPV6', - '_OPEN_MSGQ_EXT', - '_XOPEN_SOURCE_EXTENDED', - '_ALL_SOURCE', - '_LARGE_TIME_API', - '_OPEN_SYS_FILE_EXT', - '_AE_BIMODAL', - 'PATH_MAX=255' - ], - 'cflags': [ '-qxplink' ], - 'ldflags': [ '-qxplink' ], - }] + }, { + 'shared_unix_defines': [ ], + }], + ['OS in "mac ios"', { + 'shared_mac_defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], + }, { + 'shared_mac_defines': [ ], + }], + ['OS=="zos"', { + 'shared_zos_defines': [ + '_UNIX03_THREADS', + '_UNIX03_SOURCE', + '_UNIX03_WITHDRAWN', + '_OPEN_SYS_IF_EXT', + '_OPEN_SYS_SOCK_IPV6', + '_OPEN_MSGQ_EXT', + '_XOPEN_SOURCE_EXTENDED', + '_ALL_SOURCE', + '_LARGE_TIME_API', + '_OPEN_SYS_FILE_EXT', + '_AE_BIMODAL', + 'PATH_MAX=255' ], + }, { + 'shared_zos_defines': [ ], }], ], - 'xcode_settings': { - 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden - 'WARNING_CFLAGS': [ '-Wall', '-Wextra', '-Wno-unused-parameter', '-Wstrict-prototypes' ], - 'OTHER_CFLAGS': [ '-g', '--std=gnu89', '-pedantic' ], - } }, 'targets': [ @@ -49,18 +43,19 @@ 'include', 'src/', ], + 'defines': [ + '<@(shared_mac_defines)', + '<@(shared_unix_defines)', + '<@(shared_zos_defines)', + ], 'direct_dependent_settings': { + 'defines': [ + '<@(shared_mac_defines)', + '<@(shared_unix_defines)', + '<@(shared_zos_defines)', + ], 'include_dirs': [ 'include' ], 'conditions': [ - ['OS != "win"', { - 'defines': [ - '_LARGEFILE_SOURCE', - '_FILE_OFFSET_BITS=64', - ], - }], - ['OS in "mac ios"', { - 'defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], - }], ['OS == "linux"', { 'defines': [ '_POSIX_C_SOURCE=200112' ], }], @@ -83,6 +78,16 @@ 'src/uv-common.h', 'src/version.c' ], + 'xcode_settings': { + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + 'WARNING_CFLAGS': [ + '-Wall', + '-Wextra', + '-Wno-unused-parameter', + '-Wstrict-prototypes', + ], + 'OTHER_CFLAGS': [ '-g', '--std=gnu89', '-pedantic' ], + }, 'conditions': [ [ 'OS=="win"', { 'defines': [ @@ -346,273 +351,5 @@ }], ] }, - - { - 'target_name': 'run-tests', - 'type': 'executable', - 'dependencies': [ 'libuv' ], - 'sources': [ - 'test/blackhole-server.c', - 'test/echo-server.c', - 'test/run-tests.c', - 'test/runner.c', - 'test/runner.h', - 'test/test-get-loadavg.c', - 'test/task.h', - 'test/test-active.c', - 'test/test-async.c', - 'test/test-async-null-cb.c', - 'test/test-callback-stack.c', - 'test/test-callback-order.c', - 'test/test-close-fd.c', - 'test/test-close-order.c', - 'test/test-connect-unspecified.c', - 'test/test-connection-fail.c', - 'test/test-cwd-and-chdir.c', - 'test/test-default-loop-close.c', - 'test/test-delayed-accept.c', - 'test/test-eintr-handling.c', - 'test/test-error.c', - 'test/test-embed.c', - 'test/test-emfile.c', - 'test/test-env-vars.c', - 'test/test-fail-always.c', - 'test/test-fork.c', - 'test/test-fs.c', - 'test/test-fs-copyfile.c', - 'test/test-fs-event.c', - 'test/test-getters-setters.c', - 'test/test-get-currentexe.c', - 'test/test-get-memory.c', - 'test/test-get-passwd.c', - 'test/test-getaddrinfo.c', - 'test/test-gethostname.c', - 'test/test-getnameinfo.c', - 'test/test-getsockname.c', - 'test/test-handle-fileno.c', - 'test/test-homedir.c', - 'test/test-hrtime.c', - 'test/test-idle.c', - 'test/test-ip6-addr.c', - 'test/test-ipc.c', - 'test/test-ipc-send-recv.c', - 'test/test-list.h', - 'test/test-loop-handles.c', - 'test/test-loop-alive.c', - 'test/test-loop-close.c', - 'test/test-loop-stop.c', - 'test/test-loop-time.c', - 'test/test-loop-configure.c', - 'test/test-walk-handles.c', - 'test/test-watcher-cross-stop.c', - 'test/test-multiple-listen.c', - 'test/test-osx-select.c', - 'test/test-pass-always.c', - 'test/test-ping-pong.c', - 'test/test-pipe-bind-error.c', - 'test/test-pipe-connect-error.c', - 'test/test-pipe-connect-multiple.c', - 'test/test-pipe-connect-prepare.c', - 'test/test-pipe-getsockname.c', - 'test/test-pipe-pending-instances.c', - 'test/test-pipe-sendmsg.c', - 'test/test-pipe-server-close.c', - 'test/test-pipe-close-stdout-read-stdin.c', - 'test/test-pipe-set-non-blocking.c', - 'test/test-pipe-set-fchmod.c', - 'test/test-platform-output.c', - 'test/test-poll.c', - 'test/test-poll-close.c', - 'test/test-poll-close-doesnt-corrupt-stack.c', - 'test/test-poll-closesocket.c', - 'test/test-poll-oob.c', - 'test/test-process-title.c', - 'test/test-process-title-threadsafe.c', - 'test/test-queue-foreach-delete.c', - 'test/test-ref.c', - 'test/test-run-nowait.c', - 'test/test-run-once.c', - 'test/test-semaphore.c', - 'test/test-shutdown-close.c', - 'test/test-shutdown-eof.c', - 'test/test-shutdown-twice.c', - 'test/test-signal.c', - 'test/test-signal-multiple-loops.c', - 'test/test-socket-buffer-size.c', - 'test/test-spawn.c', - 'test/test-fs-poll.c', - 'test/test-stdio-over-pipes.c', - 'test/test-tcp-alloc-cb-fail.c', - 'test/test-tcp-bind-error.c', - 'test/test-tcp-bind6-error.c', - 'test/test-tcp-close.c', - 'test/test-tcp-close-accept.c', - 'test/test-tcp-close-while-connecting.c', - 'test/test-tcp-create-socket-early.c', - 'test/test-tcp-connect-error-after-write.c', - 'test/test-tcp-shutdown-after-write.c', - 'test/test-tcp-flags.c', - 'test/test-tcp-connect-error.c', - 'test/test-tcp-connect-timeout.c', - 'test/test-tcp-connect6-error.c', - 'test/test-tcp-open.c', - 'test/test-tcp-write-to-half-open-connection.c', - 'test/test-tcp-write-after-connect.c', - 'test/test-tcp-writealot.c', - 'test/test-tcp-write-fail.c', - 'test/test-tcp-try-write.c', - 'test/test-tcp-unexpected-read.c', - 'test/test-tcp-oob.c', - 'test/test-tcp-read-stop.c', - 'test/test-tcp-write-queue-order.c', - 'test/test-threadpool.c', - 'test/test-threadpool-cancel.c', - 'test/test-thread-equal.c', - 'test/test-tmpdir.c', - 'test/test-mutexes.c', - 'test/test-thread.c', - 'test/test-barrier.c', - 'test/test-condvar.c', - 'test/test-timer-again.c', - 'test/test-timer-from-check.c', - 'test/test-timer.c', - 'test/test-tty.c', - 'test/test-udp-alloc-cb-fail.c', - 'test/test-udp-bind.c', - 'test/test-udp-create-socket-early.c', - 'test/test-udp-dgram-too-big.c', - 'test/test-udp-ipv6.c', - 'test/test-udp-open.c', - 'test/test-udp-options.c', - 'test/test-udp-send-and-recv.c', - 'test/test-udp-send-hang-loop.c', - 'test/test-udp-send-immediate.c', - 'test/test-udp-send-unreachable.c', - 'test/test-udp-multicast-join.c', - 'test/test-udp-multicast-join6.c', - 'test/test-dlerror.c', - 'test/test-udp-multicast-ttl.c', - 'test/test-ip4-addr.c', - 'test/test-ip6-addr.c', - 'test/test-udp-multicast-interface.c', - 'test/test-udp-multicast-interface6.c', - 'test/test-udp-try-send.c', - ], - 'conditions': [ - [ 'OS=="win"', { - 'sources': [ - 'test/runner-win.c', - 'test/runner-win.h', - 'src/win/snprintf.c', - ], - 'libraries': [ '-lws2_32' ] - }, { # POSIX - 'sources': [ - 'test/runner-unix.c', - 'test/runner-unix.h', - ], - 'conditions': [ - [ 'OS != "zos"', { - 'defines': [ '_GNU_SOURCE' ], - 'cflags': [ '-Wno-long-long' ], - 'xcode_settings': { - 'WARNING_CFLAGS': [ '-Wno-long-long' ] - } - }], - ]}, - ], - [ 'OS in "mac dragonflybsd freebsd linux netbsd openbsd".split()', { - 'link_settings': { - 'libraries': [ '-lutil' ], - }, - }], - [ 'OS=="solaris"', { # make test-fs.c compile, needs _POSIX_C_SOURCE - 'defines': [ - '__EXTENSIONS__', - '_XOPEN_SOURCE=500', - ], - }], - [ 'OS=="aix"', { # make test-fs.c compile, needs _POSIX_C_SOURCE - 'defines': [ - '_ALL_SOURCE', - '_XOPEN_SOURCE=500', - ], - }], - ['uv_library=="shared_library"', { - 'defines': [ 'USING_UV_SHARED=1' ], - 'conditions': [ - [ 'OS == "zos"', { - 'cflags': [ '-Wc,DLL' ], - }], - ], - }], - ], - 'msvs-settings': { - 'VCLinkerTool': { - 'SubSystem': 1, # /subsystem:console - }, - }, - }, - - { - 'target_name': 'run-benchmarks', - 'type': 'executable', - 'dependencies': [ 'libuv' ], - 'sources': [ - 'test/benchmark-async.c', - 'test/benchmark-async-pummel.c', - 'test/benchmark-fs-stat.c', - 'test/benchmark-getaddrinfo.c', - 'test/benchmark-list.h', - 'test/benchmark-loop-count.c', - 'test/benchmark-million-async.c', - 'test/benchmark-million-timers.c', - 'test/benchmark-multi-accept.c', - 'test/benchmark-ping-pongs.c', - 'test/benchmark-pound.c', - 'test/benchmark-pump.c', - 'test/benchmark-sizes.c', - 'test/benchmark-spawn.c', - 'test/benchmark-thread.c', - 'test/benchmark-tcp-write-batch.c', - 'test/benchmark-udp-pummel.c', - 'test/dns-server.c', - 'test/echo-server.c', - 'test/blackhole-server.c', - 'test/run-benchmarks.c', - 'test/runner.c', - 'test/runner.h', - 'test/task.h', - ], - 'conditions': [ - [ 'OS=="win"', { - 'sources': [ - 'test/runner-win.c', - 'test/runner-win.h', - 'src/win/snprintf.c', - ], - 'libraries': [ '-lws2_32' ] - }, { # POSIX - 'defines': [ '_GNU_SOURCE' ], - 'sources': [ - 'test/runner-unix.c', - 'test/runner-unix.h', - ] - }], - ['uv_library=="shared_library"', { - 'defines': [ 'USING_UV_SHARED=1' ], - 'conditions': [ - [ 'OS == "zos"', { - 'cflags': [ '-Wc,DLL' ], - }], - ], - }], - ], - 'msvs-settings': { - 'VCLinkerTool': { - 'SubSystem': 1, # /subsystem:console - }, - }, - }, ] } diff --git a/deps/uv/vcbuild.bat b/deps/uv/vcbuild.bat index 46b3476107757a..c195394f37ea0b 100644 --- a/deps/uv/vcbuild.bat +++ b/deps/uv/vcbuild.bat @@ -159,13 +159,14 @@ goto run :msbuild-found msbuild uv.sln /t:%target% /p:Configuration=%config% /p:Platform="%msbuild_platform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 exit /b 1 +msbuild test\test.sln /t:%target% /p:Configuration=%config% /p:Platform="%msbuild_platform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +if errorlevel 1 exit /b 1 :run @rem Run tests if requested. if "%run%"=="" goto exit -if not exist %config%\%run% goto exit -echo running '%config%\%run%' -%config%\%run% +echo running 'test\%config%\%run%' +test\%config%\%run% goto exit :create-msvs-files-failed From 0075f8c231fc1a0420f3c04d0c82bc7f491d8cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Fri, 23 Feb 2018 10:28:23 +0100 Subject: [PATCH 19/47] tools: ignore VS compiler output in deps/v8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/18952 Reviewed-By: Ben Noordhuis Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater Reviewed-By: Tobias NieƟen Reviewed-By: Colin Ihrig --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0ff301ace3824d..a9d428ad6abafc 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,8 @@ deps/uv/docs/src/guide/ # do not override V8's .gitignore !deps/v8/** +# ignore VS compiler output unhandled by V8's .gitignore +deps/v8/src/Debug/ +deps/v8/src/Release/ +deps/v8/src/inspector/Debug/ +deps/v8/src/inspector/Release/ From 89323da40fcb7fa602b2e5485d902368f80f1de2 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Feb 2018 15:42:20 +0100 Subject: [PATCH 20/47] src: remove node namespace qualifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit removes unneccessary node namespace qualifiers in node.cc for consistency. PR-URL: https://github.com/nodejs/node/pull/18962 Reviewed-By: Colin Ihrig Reviewed-By: MichaĆ«l Zasso Reviewed-By: Anna Henningsen Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Jeremiah Senkpiel --- src/node.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/node.cc b/src/node.cc index 0a466d20c90d88..5fbbe1cf9f824c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -173,7 +173,7 @@ using v8::Undefined; using v8::V8; using v8::Value; -using AsyncHooks = node::Environment::AsyncHooks; +using AsyncHooks = Environment::AsyncHooks; static bool print_eval = false; static bool force_repl = false; @@ -274,7 +274,7 @@ static double prog_start_time; static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; -node::DebugOptions debug_options; +DebugOptions debug_options; static struct { #if NODE_USE_V8_PLATFORM @@ -312,7 +312,7 @@ static struct { #if HAVE_INSPECTOR bool StartInspector(Environment *env, const char* script_path, - const node::DebugOptions& options) { + const DebugOptions& options) { // Inspector agent can't fail to start, but if it was configured to listen // right away on the websocket port and fails to bind/etc, this will return // false. @@ -344,7 +344,7 @@ static struct { void DrainVMTasks(Isolate* isolate) {} void CancelVMTasks(Isolate* isolate) {} bool StartInspector(Environment *env, const char* script_path, - const node::DebugOptions& options) { + const DebugOptions& options) { env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); return true; } @@ -1108,9 +1108,9 @@ Local WinapiErrnoException(Isolate* isolate, void* ArrayBufferAllocator::Allocate(size_t size) { if (zero_fill_field_ || zero_fill_all_buffers) - return node::UncheckedCalloc(size); + return UncheckedCalloc(size); else - return node::UncheckedMalloc(size); + return UncheckedMalloc(size); } namespace { @@ -4449,7 +4449,7 @@ void Init(int* argc, prog_start_time = static_cast(uv_now(uv_default_loop())); // Register built-in modules - node::RegisterBuiltinModules(); + RegisterBuiltinModules(); // Make inherited handles noninheritable. uv_disable_stdio_inheritance(); @@ -4799,7 +4799,7 @@ inline int Start(uv_loop_t* event_loop, int Start(int argc, char** argv) { atexit([] () { uv_tty_reset_mode(); }); PlatformInit(); - node::performance::performance_node_start = PERFORMANCE_NOW(); + performance::performance_node_start = PERFORMANCE_NOW(); CHECK_GT(argc, 0); @@ -4836,7 +4836,7 @@ int Start(int argc, char** argv) { v8_platform.StartTracingAgent(); } V8::Initialize(); - node::performance::performance_v8_start = PERFORMANCE_NOW(); + performance::performance_v8_start = PERFORMANCE_NOW(); v8_initialized = true; const int exit_code = Start(uv_default_loop(), argc, argv, exec_argc, exec_argv); From eb4ab485a2f9c24dc4f925dbd434deae36e57610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=88=9A?= Date: Mon, 22 Jan 2018 00:58:35 +0800 Subject: [PATCH 21/47] doc: `readable.push(undefined)` in non-object mode `readable.push()` supports `undefined` in non-object mode, but it was not previously documented. PR-URL: https://github.com/nodejs/node/pull/18283 Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/stream.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/api/stream.md b/doc/api/stream.md index ae0093dd58ece5..424f3cfc33924c 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1749,6 +1749,10 @@ class SourceWrapper extends Readable { *Note*: The `readable.push()` method is intended be called only by Readable Implementers, and only from within the `readable._read()` method. +For streams not operating in object mode, if the `chunk` parameter of +`readable.push()` is `undefined`, it will be treated as empty string or +buffer. See [`readable.push('')`][] for more information. + #### Errors While Reading It is recommended that errors occurring during the processing of the @@ -2264,6 +2268,7 @@ contain multi-byte characters. [`stream.uncork()`]: #stream_writable_uncork [`stream.unpipe()`]: #stream_readable_unpipe_destination [`stream.wrap()`]: #stream_readable_wrap_stream +[`readable.push('')`]: #stream_readable_push [`writable.cork()`]: #stream_writable_cork [`writable.uncork()`]: #stream_writable_uncork [`zlib.createDeflate()`]: zlib.html#zlib_zlib_createdeflate_options From b5ecc459c4176d8526f17372e0767c4e98db97ea Mon Sep 17 00:00:00 2001 From: flickz Date: Sun, 11 Feb 2018 20:21:09 +0100 Subject: [PATCH 22/47] doc: add process.debugPort to doc/api/process.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/18639 PR-URL: https://github.com/nodejs/node/pull/18716 Refs: https://github.com/nodejs/node/issues/18639 Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Matheus Marchini Reviewed-By: Benjamin Gruenbaum Reviewed-By: Tobias NieƟen --- doc/api/process.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/api/process.md b/doc/api/process.md index 8e0da34110b437..c0cac3b641580f 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -621,7 +621,17 @@ process. ```js console.log(`Current directory: ${process.cwd()}`); ``` +## process.debugPort + +* {number} +The port used by Node.js's debugger when enabled. + +```js +process.debugPort = 5858; +``` ## process.disconnect() > Stability: 0 - Deprecated: Use [`asyncResource.runInAsyncScope()`][] instead. -* Returns: {undefined} - Call all `before` callbacks to notify that a new asynchronous execution context is being entered. If nested calls to `emitBefore()` are made, the stack of `asyncId`s will be tracked and properly unwound. @@ -704,8 +702,6 @@ deprecated: v9.6.0 --> > Stability: 0 - Deprecated: Use [`asyncResource.runInAsyncScope()`][] instead. -* Returns: {undefined} - Call all `after` callbacks. If nested calls to `emitBefore()` were made, then make sure the stack is unwound properly. Otherwise an error will be thrown. @@ -721,8 +717,6 @@ alternative. #### `asyncResource.emitDestroy()` -* Returns: {undefined} - Call all `destroy` hooks. This should only ever be called once. An error will be thrown if it is called more than once. This **must** be manually called. If the resource is left to be collected by the GC then the `destroy` hooks will diff --git a/doc/api/fs.md b/doc/api/fs.md index dccc0d06601817..dee32e8696a7c2 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -788,7 +788,6 @@ changes: * `path` {string|Buffer|URL} * `mode` {integer} **Default:** `fs.constants.F_OK` -* Returns: {undefined} Synchronously tests a user's permissions for the file or directory specified by `path`. The `mode` argument is an optional integer that specifies the diff --git a/doc/api/http2.md b/doc/api/http2.md index 2a6e63c64e0da4..07a4e8abf6ba1f 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -328,7 +328,6 @@ added: v8.4.0 * `code` {number} The HTTP/2 error code to send in the final `GOAWAY` frame. If unspecified, and `error` is not undefined, the default is `INTERNAL_ERROR`, otherwise defaults to `NO_ERROR`. -* Returns: {undefined} Immediately terminates the `Http2Session` and the associated `net.Socket` or `tls.TLSSocket`. @@ -471,7 +470,6 @@ added: v8.4.0 * `msecs` {number} * `callback` {Function} -* Returns: {undefined} Used to set a callback function that is called when there is no activity on the `Http2Session` after `msecs` milliseconds. The given `callback` is @@ -531,7 +529,6 @@ added: v8.4.0 --> * `settings` {HTTP2 Settings Object} -* Returns {undefined} Updates the current local settings for this `Http2Session` and sends a new `SETTINGS` frame to the connected HTTP/2 peer. @@ -886,7 +883,6 @@ added: v8.4.0 `http2.constants.NGHTTP2_NO_ERROR` (`0x00`) * `callback` {Function} An optional function registered to listen for the `'close'` event. -* Returns: {undefined} Closes the `Http2Stream` instance by sending an `RST_STREAM` frame to the connected HTTP/2 peer. @@ -937,7 +933,6 @@ added: v8.4.0 and `256` (inclusive). * `silent` {boolean} When `true`, changes the priority locally without sending a `PRIORITY` frame to the connected peer. -* Returns: {undefined} Updates the priority for this `Http2Stream` instance. @@ -998,7 +993,6 @@ added: v8.4.0 * `msecs` {number} * `callback` {Function} -* Returns: {undefined} ```js const http2 = require('http2'); @@ -1121,7 +1115,6 @@ added: v8.4.0 --> * `headers` {HTTP2 Headers Object} -* Returns: {undefined} Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer. @@ -1165,7 +1158,6 @@ added: v8.4.0 * `pushStream` {ServerHttp2Stream} The returned pushStream object. * `headers` {HTTP2 Headers Object} Headers object the pushStream was initiated with. -* Returns: {undefined} Initiates a push stream. The callback is invoked with the new `Http2Stream` instance created for the push stream passed as the second argument, or an @@ -1200,7 +1192,6 @@ added: v8.4.0 include payload data. * `getTrailers` {Function} Callback function invoked to collect trailer headers. -* Returns: {undefined} ```js const http2 = require('http2'); From 534de4aad098fe22303b1c56143ac3477d36c5cb Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Mon, 16 Oct 2017 15:34:42 +0200 Subject: [PATCH 26/47] build: make gyp user defined variables lowercase I mistakenly introduced user defined variables using uppercase characters, reading the gyp documentation they state: "Predefined variables. By convention, these are named with CAPITAL_LETTERS. Predefined variables are set automatically by GYP" and also "By convention, user-defined variables are named with lowercase_letters." This commit renames the user defined variables to lowercase to follow the above mentioned convention. Backport-PR-URL: https://github.com/nodejs/node/pull/18899 PR-URL: https://github.com/nodejs/node/pull/16238 Reviewed-By: Ben Noordhuis Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- common.gypi | 20 +-- node.gyp | 126 +++++++++--------- node.gypi | 14 +- .../openssl-client-cert-engine/binding.gyp | 2 +- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/common.gypi b/common.gypi index a08e7f487f3b3e..5752c17168d50a 100644 --- a/common.gypi +++ b/common.gypi @@ -44,29 +44,29 @@ 'conditions': [ ['GENERATOR=="ninja"', { - 'OBJ_DIR': '<(PRODUCT_DIR)/obj', - 'V8_BASE': '<(PRODUCT_DIR)/obj/deps/v8/src/libv8_base.a', + 'obj_dir': '<(PRODUCT_DIR)/obj', + 'v8_base': '<(PRODUCT_DIR)/obj/deps/v8/src/libv8_base.a', }, { - 'OBJ_DIR%': '<(PRODUCT_DIR)/obj.target', - 'V8_BASE%': '<(PRODUCT_DIR)/obj.target/deps/v8/src/libv8_base.a', + 'obj_dir%': '<(PRODUCT_DIR)/obj.target', + 'v8_base%': '<(PRODUCT_DIR)/obj.target/deps/v8/src/libv8_base.a', }], ['OS == "win"', { 'os_posix': 0, 'v8_postmortem_support%': 'false', - 'OBJ_DIR': '<(PRODUCT_DIR)/obj', - 'V8_BASE': '<(PRODUCT_DIR)/lib/v8_libbase.lib', + 'obj_dir': '<(PRODUCT_DIR)/obj', + 'v8_base': '<(PRODUCT_DIR)/lib/v8_libbase.lib', }, { 'os_posix': 1, 'v8_postmortem_support%': 'true', }], ['OS== "mac"', { - 'OBJ_DIR%': '<(PRODUCT_DIR)/obj.target', - 'V8_BASE': '<(PRODUCT_DIR)/libv8_base.a', + 'obj_dir%': '<(PRODUCT_DIR)/obj.target', + 'v8_base': '<(PRODUCT_DIR)/libv8_base.a', }], ['openssl_fips != ""', { - 'OPENSSL_PRODUCT': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)', + 'openssl_product': '<(STATIC_LIB_PREFIX)crypto<(STATIC_LIB_SUFFIX)', }, { - 'OPENSSL_PRODUCT': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', + 'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)', }], ['OS=="mac"', { 'clang%': 1, diff --git a/node.gyp b/node.gyp index 0e9db9a16eea9d..4b1d0b11aa9ab6 100644 --- a/node.gyp +++ b/node.gyp @@ -229,7 +229,7 @@ 'conditions': [ ['OS in "linux freebsd openbsd solaris android"', { 'ldflags': [ - '-Wl,--whole-archive,<(OBJ_DIR)/<(STATIC_LIB_PREFIX)' + '-Wl,--whole-archive,<(obj_dir)/<(STATIC_LIB_PREFIX)' '<(node_core_target_name)<(STATIC_LIB_SUFFIX)', '-Wl,--no-whole-archive', ], @@ -773,10 +773,10 @@ { 'action_name': 'node_dtrace_provider_o', 'inputs': [ - '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace.o', + '<(obj_dir)/<(node_lib_target_name)/src/node_dtrace.o', ], 'outputs': [ - '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace_provider.o' + '<(obj_dir)/<(node_lib_target_name)/src/node_dtrace_provider.o' ], 'action': [ 'dtrace', '-G', '-xnolibs', '-s', 'src/node_provider.d', '<@(_inputs)', '-o', '<@(_outputs)' ] @@ -808,7 +808,7 @@ { 'action_name': 'node_dtrace_ustack_constants', 'inputs': [ - '<(V8_BASE)' + '<(v8_base)' ], 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/v8constants.h' @@ -826,7 +826,7 @@ '<(SHARED_INTERMEDIATE_DIR)/v8constants.h' ], 'outputs': [ - '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace_ustack.o' + '<(obj_dir)/<(node_lib_target_name)/src/node_dtrace_ustack.o' ], 'conditions': [ [ 'target_arch=="ia32" or target_arch=="arm"', { @@ -916,32 +916,32 @@ ], 'variables': { - 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src', - 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/gen', - 'OBJ_TRACING_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src/tracing', - 'OBJ_SUFFIX': 'o', - 'OBJ_SEPARATOR': '/', + 'obj_path': '<(obj_dir)/<(node_lib_target_name)/src', + 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)/gen', + 'obj_tracing_path': '<(obj_dir)/<(node_lib_target_name)/src/tracing', + 'obj_suffix': 'o', + 'obj_separator': '/', 'conditions': [ ['OS=="win"', { - 'OBJ_SUFFIX': 'obj', + 'obj_suffix': 'obj', }], ['GENERATOR=="ninja"', { - 'OBJ_PATH': '<(OBJ_DIR)/src', - 'OBJ_GEN_PATH': '<(OBJ_DIR)/gen', - 'OBJ_TRACING_PATH': '<(OBJ_DIR)/src/tracing', - 'OBJ_SEPARATOR': '/<(node_lib_target_name).', + 'obj_path': '<(obj_dir)/src', + 'obj_gen_path': '<(obj_dir)/gen', + 'obj_tracing_path': '<(obj_dir)/src/tracing', + 'obj_separator': '/<(node_lib_target_name).', }, { 'conditions': [ ['OS=="win"', { - 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)', - 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)', - 'OBJ_TRACING_PATH': '<(OBJ_DIR)/<(node_lib_target_name)', + 'obj_path': '<(obj_dir)/<(node_lib_target_name)', + 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)', + 'obj_tracing_path': '<(obj_dir)/<(node_lib_target_name)', }], ['OS=="aix"', { - 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src', - 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/gen', - 'OBJ_TRACING_PATH': - '<(OBJ_DIR)/<(node_lib_target_name)/src/tracing', + 'obj_path': '<(obj_dir)/<(node_lib_target_name)/src', + 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)/gen', + 'obj_tracing_path': + '<(obj_dir)/<(node_lib_target_name)/src/tracing', }], ]} ] @@ -974,25 +974,25 @@ ], 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)async_wrap.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_platform.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)', - '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)trace_event.<(OBJ_SUFFIX)', - '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)async_wrap.<(obj_suffix)', + '<(obj_path)<(obj_separator)env.<(obj_suffix)', + '<(obj_path)<(obj_separator)node.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_buffer.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_debug_options.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_i18n.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_perf.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_platform.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_url.<(obj_suffix)', + '<(obj_path)<(obj_separator)util.<(obj_suffix)', + '<(obj_path)<(obj_separator)string_bytes.<(obj_suffix)', + '<(obj_path)<(obj_separator)string_search.<(obj_suffix)', + '<(obj_path)<(obj_separator)stream_base.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_constants.<(obj_suffix)', + '<(obj_tracing_path)<(obj_separator)agent.<(obj_suffix)', + '<(obj_tracing_path)<(obj_separator)node_trace_buffer.<(obj_suffix)', + '<(obj_tracing_path)<(obj_separator)node_trace_writer.<(obj_suffix)', + '<(obj_tracing_path)<(obj_separator)trace_event.<(obj_suffix)', + '<(obj_gen_path)<(obj_separator)node_javascript.<(obj_suffix)', ], 'conditions': [ @@ -1000,10 +1000,10 @@ 'conditions': [ ['node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_crypto.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_crypto_bio.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_crypto_clienthello.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)tls_wrap.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)node_crypto.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_crypto_bio.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_crypto_clienthello.<(obj_suffix)', + '<(obj_path)<(obj_separator)tls_wrap.<(obj_suffix)', ], }], ], @@ -1014,9 +1014,9 @@ [ 'node_use_perfctr=="true"', { 'defines': [ 'HAVE_PERFCTR=1' ], 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_counters.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)' - 'node_win32_perfctr_provider.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)node_counters.<(obj_suffix)', + '<(obj_path)<(obj_separator)' + 'node_win32_perfctr_provider.<(obj_suffix)', ], }], ['v8_enable_inspector==1', { @@ -1027,11 +1027,11 @@ 'conditions': [ ['node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)inspector_agent.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)inspector_io.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)inspector_js_api.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)inspector_socket.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)inspector_socket_server.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)inspector_agent.<(obj_suffix)', + '<(obj_path)<(obj_separator)inspector_io.<(obj_suffix)', + '<(obj_path)<(obj_separator)inspector_js_api.<(obj_suffix)', + '<(obj_path)<(obj_separator)inspector_socket.<(obj_suffix)', + '<(obj_path)<(obj_separator)inspector_socket_server.<(obj_suffix)', ], }], ], @@ -1041,19 +1041,19 @@ }], [ 'node_use_dtrace=="true" and node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_dtrace.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)node_dtrace.<(obj_suffix)', ], 'conditions': [ ['OS!="mac" and OS!="linux"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_dtrace_provider.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_dtrace_ustack.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)node_dtrace_provider.<(obj_suffix)', + '<(obj_path)<(obj_separator)node_dtrace_ustack.<(obj_suffix)', ] }], ['OS=="linux"', { 'libraries': [ - '<(SHARED_INTERMEDIATE_DIR)<(OBJ_SEPARATOR)' - 'node_dtrace_provider.<(OBJ_SUFFIX)', + '<(SHARED_INTERMEDIATE_DIR)<(obj_separator)' + 'node_dtrace_provider.<(obj_suffix)', ] }], ], @@ -1061,22 +1061,22 @@ 'conditions': [ [ 'node_use_etw=="true" and OS=="win"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)node_dtrace.<(OBJ_SUFFIX)', - '<(OBJ_PATH)<(OBJ_SEPARATOR)' - 'node_win32_etw_provider.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)node_dtrace.<(obj_suffix)', + '<(obj_path)<(obj_separator)' + 'node_win32_etw_provider.<(obj_suffix)', ], }] ] }], [ 'OS=="win" and node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)backtrace_win32.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)backtrace_win32.<(obj_suffix)', ], }, { 'conditions': [ ['node_target_type!="static_library"', { 'libraries': [ - '<(OBJ_PATH)<(OBJ_SEPARATOR)backtrace_posix.<(OBJ_SUFFIX)', + '<(obj_path)<(obj_separator)backtrace_posix.<(obj_suffix)', ], }], ], diff --git a/node.gypi b/node.gypi index 386601906fbe4a..49384db0a5ddb5 100644 --- a/node.gypi +++ b/node.gypi @@ -99,7 +99,7 @@ [ 'force_load=="true"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ - '-Wl,-force_load,<(V8_BASE)', + '-Wl,-force_load,<(v8_base)', ], }, }], @@ -154,7 +154,7 @@ { 'action_name': 'expfile', 'inputs': [ - '<(OBJ_DIR)' + '<(obj_dir)' ], 'outputs': [ '<(PRODUCT_DIR)/node.exp' @@ -186,13 +186,13 @@ [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"' ' and coverage=="false" and force_load=="true"', { 'ldflags': [ '-Wl,-z,noexecstack', - '-Wl,--whole-archive <(V8_BASE)', + '-Wl,--whole-archive <(v8_base)', '-Wl,--no-whole-archive' ] }], [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"' ' and coverage=="true" and force_load=="true"', { 'ldflags': [ '-Wl,-z,noexecstack', - '-Wl,--whole-archive <(V8_BASE)', + '-Wl,--whole-archive <(v8_base)', '-Wl,--no-whole-archive', '--coverage', '-g', @@ -237,15 +237,15 @@ [ 'force_load=="true"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ - '-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)', + '-Wl,-force_load,<(PRODUCT_DIR)/<(openssl_product)', ], }, 'conditions': [ ['OS in "linux freebsd" and node_shared=="false"', { 'ldflags': [ '-Wl,--whole-archive,' - '<(OBJ_DIR)/deps/openssl/' - '<(OPENSSL_PRODUCT)', + '<(obj_dir)/deps/openssl/' + '<(openssl_product)', '-Wl,--no-whole-archive', ], }], diff --git a/test/addons/openssl-client-cert-engine/binding.gyp b/test/addons/openssl-client-cert-engine/binding.gyp index b069e43429c12b..f43be602199ad0 100644 --- a/test/addons/openssl-client-cert-engine/binding.gyp +++ b/test/addons/openssl-client-cert-engine/binding.gyp @@ -14,7 +14,7 @@ 'include_dirs': ['../../../deps/openssl/openssl/include'], 'link_settings': { 'libraries': [ - '../../../../out/<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)' + '../../../../out/<(PRODUCT_DIR)/<(openssl_product)' ] }, }] From 70c9ad9aefaa8ec4f0649c725b25a637f89d5e57 Mon Sep 17 00:00:00 2001 From: killagu Date: Sun, 11 Feb 2018 11:50:31 +0800 Subject: [PATCH 27/47] tools, test: fix prof polyfill readline `node --prof foo.js` may not print the full profile log file, leaving the last line broken (for example `tick,`. When that happens, `readline` will be stuck in an infinite loop. This patch fixes it. Also introduced `common.isCPPSymbolsNotMapped` to avoid duplicated code on tick-processor tests. Backport-PR-URL: https://github.com/nodejs/node/pull/18901 PR-URL: https://github.com/nodejs/node/pull/18641 Reviewed-By: Khaidi Chu Reviewed-By: Matheus Marchini Reviewed-By: Ruben Bridgewater --- lib/internal/v8_prof_polyfill.js | 7 +++ test/common/README.md | 5 ++ test/common/index.js | 6 ++ .../test-tick-processor-builtin.js | 7 +-- .../test-tick-processor-cpp-core.js | 7 +-- ...test-tick-processor-polyfill-brokenfile.js | 62 +++++++++++++++++++ .../test-tick-processor-preprocess-flag.js | 7 +-- 7 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 test/tick-processor/test-tick-processor-polyfill-brokenfile.js diff --git a/lib/internal/v8_prof_polyfill.js b/lib/internal/v8_prof_polyfill.js index 5c6b1407120ea2..43ccc0e5d8bfac 100644 --- a/lib/internal/v8_prof_polyfill.js +++ b/lib/internal/v8_prof_polyfill.js @@ -96,6 +96,13 @@ function readline() { if (line.length === 0) { return ''; } + if (bytes === 0) { + process.emitWarning(`Profile file ${logFile} is broken`, { + code: 'BROKEN_PROFILE_FILE', + detail: `${JSON.stringify(line)} at the file end is broken` + }); + return ''; + } } } diff --git a/test/common/README.md b/test/common/README.md index f1e4c329ed1b9b..c6742deb691587 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -243,6 +243,11 @@ Platform check for Windows. Platform check for Windows 32-bit on Windows 64-bit. +### isCPPSymbolsNotMapped +* [<Boolean>] + +Platform check for C++ symbols are mapped or not. + ### leakedGlobals() * return [<Array>] diff --git a/test/common/index.js b/test/common/index.js index 30b6aca88e05e3..ce07d91c80224c 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -832,3 +832,9 @@ exports.firstInvalidFD = function firstInvalidFD() { } catch (e) {} return fd; }; + +exports.isCPPSymbolsNotMapped = exports.isWindows || + exports.isSunOS || + exports.isAIX || + exports.isLinuxPPCBE || + exports.isFreeBSD; diff --git a/test/tick-processor/test-tick-processor-builtin.js b/test/tick-processor/test-tick-processor-builtin.js index f94964813ac76a..3d4e1b9d236030 100644 --- a/test/tick-processor/test-tick-processor-builtin.js +++ b/test/tick-processor/test-tick-processor-builtin.js @@ -4,12 +4,9 @@ const common = require('../common'); if (!common.enoughTestCpu) common.skip('test is CPU-intensive'); -if (common.isWindows || - common.isSunOS || - common.isAIX || - common.isLinuxPPCBE || - common.isFreeBSD) +if (common.isCPPSymbolsNotMapped) { common.skip('C++ symbols are not mapped for this os.'); +} const base = require('./tick-processor-base.js'); diff --git a/test/tick-processor/test-tick-processor-cpp-core.js b/test/tick-processor/test-tick-processor-cpp-core.js index 496d06b555f3e8..7775dcdbd6184a 100644 --- a/test/tick-processor/test-tick-processor-cpp-core.js +++ b/test/tick-processor/test-tick-processor-cpp-core.js @@ -4,12 +4,9 @@ const common = require('../common'); if (!common.enoughTestCpu) common.skip('test is CPU-intensive'); -if (common.isWindows || - common.isSunOS || - common.isAIX || - common.isLinuxPPCBE || - common.isFreeBSD) +if (common.isCPPSymbolsNotMapped) { common.skip('C++ symbols are not mapped for this os.'); +} const base = require('./tick-processor-base.js'); diff --git a/test/tick-processor/test-tick-processor-polyfill-brokenfile.js b/test/tick-processor/test-tick-processor-polyfill-brokenfile.js new file mode 100644 index 00000000000000..3348b6f11b2e67 --- /dev/null +++ b/test/tick-processor/test-tick-processor-polyfill-brokenfile.js @@ -0,0 +1,62 @@ +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +if (!common.enoughTestCpu) + common.skip('test is CPU-intensive'); + +if (common.isCPPSymbolsNotMapped) { + common.skip('C++ symbols are not mapped for this OS.'); +} + +// This test will produce a broken profile log. +// ensure prof-polyfill not stuck in infinite loop +// and success process + + +const assert = require('assert'); +const cp = require('child_process'); +const path = require('path'); +const fs = require('fs'); + +const LOG_FILE = path.join(tmpdir.path, 'tick-processor.log'); +const RETRY_TIMEOUT = 150; +const BROKEN_PART = 'tick,'; +const WARN_REG_EXP = /\(node:\d+\) \[BROKEN_PROFILE_FILE] Warning: Profile file .* is broken/; +const WARN_DETAIL_REG_EXP = /".*tick," at the file end is broken/; + +const code = `function f() { + this.ts = Date.now(); + setImmediate(function() { new f(); }); + }; + f();`; + +const proc = cp.spawn(process.execPath, [ + '--no_logfile_per_isolate', + '--logfile=-', + '--prof', + '-pe', code +], { + stdio: ['ignore', 'pipe', 'inherit'] +}); + +let ticks = ''; +proc.stdout.on('data', (chunk) => ticks += chunk); + + +function runPolyfill(content) { + proc.kill(); + content += BROKEN_PART; + fs.writeFileSync(LOG_FILE, content); + const child = cp.spawnSync( + `${process.execPath}`, + [ + '--prof-process', LOG_FILE + ]); + assert(WARN_REG_EXP.test(child.stderr.toString())); + assert(WARN_DETAIL_REG_EXP.test(child.stderr.toString())); + assert.strictEqual(child.status, 0); +} + +setTimeout(() => runPolyfill(ticks), RETRY_TIMEOUT); diff --git a/test/tick-processor/test-tick-processor-preprocess-flag.js b/test/tick-processor/test-tick-processor-preprocess-flag.js index 52d642a3ae3fc0..8b852d6a83d180 100644 --- a/test/tick-processor/test-tick-processor-preprocess-flag.js +++ b/test/tick-processor/test-tick-processor-preprocess-flag.js @@ -4,12 +4,9 @@ const common = require('../common'); if (!common.enoughTestCpu) common.skip('test is CPU-intensive'); -if (common.isWindows || - common.isSunOS || - common.isAIX || - common.isLinuxPPCBE || - common.isFreeBSD) +if (common.isCPPSymbolsNotMapped) { common.skip('C++ symbols are not mapped for this os.'); +} const base = require('./tick-processor-base.js'); From 8beecb2f459a4f25ca0377e276e7d98b62bab894 Mon Sep 17 00:00:00 2001 From: Yihong Wang Date: Wed, 17 Jan 2018 13:16:14 -0800 Subject: [PATCH 28/47] build: include the libuv and zlib into node Add libuv and zlib into node executable and shared lib. Also fix an issue that openssl is not fully included in node executable for macOS. Signed-off-by: Yihong Wang Fixes: https://github.com/nodejs/node/issues/17444 PR-URL: https://github.com/nodejs/node/pull/18383 Reviewed-By: Anna Henningsen Reviewed-By: Gireesh Punathil Reviewed-By: Ben Noordhuis Reviewed-By: Michael Dawson Reviewed-By: James M Snell --- node.gyp | 2 +- node.gypi | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/node.gyp b/node.gyp index 4b1d0b11aa9ab6..3320e3b4cd122c 100644 --- a/node.gyp +++ b/node.gyp @@ -227,7 +227,7 @@ }, }, 'conditions': [ - ['OS in "linux freebsd openbsd solaris android"', { + ['OS!="aix"', { 'ldflags': [ '-Wl,--whole-archive,<(obj_dir)/<(STATIC_LIB_PREFIX)' '<(node_core_target_name)<(STATIC_LIB_SUFFIX)', diff --git a/node.gypi b/node.gypi index 49384db0a5ddb5..82953ee9ff9af4 100644 --- a/node.gypi +++ b/node.gypi @@ -107,6 +107,32 @@ }], [ 'node_shared_zlib=="false"', { 'dependencies': [ 'deps/zlib/zlib.gyp:zlib' ], + 'conditions': [ + [ 'force_load=="true"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-Wl,-force_load,<(PRODUCT_DIR)/<(STATIC_LIB_PREFIX)' + 'zlib<(STATIC_LIB_SUFFIX)', + ], + }, + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\zlib<(STATIC_LIB_SUFFIX)', + ], + }, + }, + 'conditions': [ + ['OS!="aix" and node_shared=="false"', { + 'ldflags': [ + '-Wl,--whole-archive,<(obj_dir)/deps/zlib/<(STATIC_LIB_PREFIX)' + 'zlib<(STATIC_LIB_SUFFIX)', + '-Wl,--no-whole-archive', + ], + }], + ], + }], + ], }], [ 'node_shared_http_parser=="false"', { @@ -119,6 +145,32 @@ [ 'node_shared_libuv=="false"', { 'dependencies': [ 'deps/uv/uv.gyp:libuv' ], + 'conditions': [ + [ 'force_load=="true"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-Wl,-force_load,<(PRODUCT_DIR)/<(STATIC_LIB_PREFIX)' + 'uv<(STATIC_LIB_SUFFIX)', + ], + }, + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\libuv<(STATIC_LIB_SUFFIX)', + ], + }, + }, + 'conditions': [ + ['OS!="aix" and node_shared=="false"', { + 'ldflags': [ + '-Wl,--whole-archive,<(obj_dir)/deps/uv/<(STATIC_LIB_PREFIX)' + 'uv<(STATIC_LIB_SUFFIX)', + '-Wl,--no-whole-archive', + ], + }], + ], + }], + ], }], [ 'node_shared_nghttp2=="false"', { @@ -240,12 +292,18 @@ '-Wl,-force_load,<(PRODUCT_DIR)/<(openssl_product)', ], }, + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\<(openssl_product)', + ], + }, + }, 'conditions': [ ['OS in "linux freebsd" and node_shared=="false"', { 'ldflags': [ '-Wl,--whole-archive,' - '<(obj_dir)/deps/openssl/' - '<(openssl_product)', + '<(obj_dir)/deps/openssl/<(openssl_product)', '-Wl,--no-whole-archive', ], }], From 61436e826e19ab443ccd18a181b729fc8c1d2d4c Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 31 Dec 2017 00:27:56 +0100 Subject: [PATCH 29/47] process: use more direct sync I/O for stdio This avoids routing writes through the full LibuvStreamWrap write machinery. In particular, it enables the next commit, because otherwise the callback passed to `_write()` would not be called synchronously for pipes on Windows (because the latter does not support `uv_try_write()`, even for blocking I/O). PR-URL: https://github.com/nodejs/node/pull/18019 Reviewed-By: Anatoli Papirovski Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- lib/internal/net.js | 24 ++++++++++++++++++++++++ lib/net.js | 16 ++++++++++++---- lib/tty.js | 3 +++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/internal/net.js b/lib/internal/net.js index d4558cfca8e214..847539d576906d 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -1,5 +1,8 @@ 'use strict'; +const Buffer = require('buffer').Buffer; +const { writeBuffer } = process.binding('fs'); + // Check that the port number is not NaN when coerced to a number, // is an integer and that it falls within the legal range of port numbers. function isLegalPort(port) { @@ -9,7 +12,28 @@ function isLegalPort(port) { return +port === (+port >>> 0) && port <= 0xFFFF; } +function makeSyncWrite(fd) { + return function(chunk, enc, cb) { + if (enc !== 'buffer') + chunk = Buffer.from(chunk, enc); + + this._bytesDispatched += chunk.length; + + try { + writeBuffer(fd, chunk, 0, chunk.length, null); + } catch (ex) { + // Legacy: net writes have .code === .errno, whereas writeBuffer gives the + // raw errno number in .errno. + if (typeof ex.code === 'string') + ex.errno = ex.code; + return cb(ex); + } + cb(); + }; +} + module.exports = { isLegalPort, + makeSyncWrite, normalizedArgsSymbol: Symbol('normalizedArgs') }; diff --git a/lib/net.js b/lib/net.js index f291e0555bdbc3..95dd6615b39c86 100644 --- a/lib/net.js +++ b/lib/net.js @@ -26,7 +26,11 @@ const stream = require('stream'); const timers = require('timers'); const util = require('util'); const internalUtil = require('internal/util'); -const { isLegalPort, normalizedArgsSymbol } = require('internal/net'); +const { + isLegalPort, + normalizedArgsSymbol, + makeSyncWrite +} = require('internal/net'); const assert = require('assert'); const cares = process.binding('cares_wrap'); const { @@ -213,20 +217,24 @@ function Socket(options) { this._handle = options.handle; // private this[async_id_symbol] = getNewAsyncId(this._handle); } else if (options.fd !== undefined) { - this._handle = createHandle(options.fd, false); - this._handle.open(options.fd); + const fd = options.fd; + this._handle = createHandle(fd, false); + this._handle.open(fd); this[async_id_symbol] = this._handle.getAsyncId(); // options.fd can be string (since it is user-defined), // so changing this to === would be semver-major // See: https://github.com/nodejs/node/pull/11513 // eslint-disable-next-line eqeqeq - if ((options.fd == 1 || options.fd == 2) && + if ((fd == 1 || fd == 2) && (this._handle instanceof Pipe) && process.platform === 'win32') { // Make stdout and stderr blocking on Windows var err = this._handle.setBlocking(true); if (err) throw errnoException(err, 'setBlocking'); + + this._writev = null; + this._write = makeSyncWrite(fd); } this.readable = options.readable !== false; this.writable = options.writable !== false; diff --git a/lib/tty.js b/lib/tty.js index 9595c79db39a33..7b93309efc3d99 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -24,6 +24,7 @@ const util = require('util'); const net = require('net'); const { TTY, isTTY } = process.binding('tty_wrap'); +const { makeSyncWrite } = require('internal/net'); const { inherits } = util; const errnoException = util._errnoException; const errors = require('internal/errors'); @@ -79,6 +80,8 @@ function WriteStream(fd) { // even though it was originally intended to change in v1.0.2 (Libuv 1.2.1). // Ref: https://github.com/nodejs/node/pull/1771#issuecomment-119351671 this._handle.setBlocking(true); + this._writev = null; + this._write = makeSyncWrite(fd); var winSize = new Array(2); var err = this._handle.getWindowSize(winSize); From d2a752ff63ee00a334f46dfccefd5c88e32e151e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sat, 30 Dec 2017 12:40:54 +0100 Subject: [PATCH 30/47] src: remove `HasWriteQueue()` Tests are passing without it, and this otherwise makes the code harder to reason about because the `async` flag on the write request object would not be set even though the callback would still be pending. PR-URL: https://github.com/nodejs/node/pull/18019 Reviewed-By: Anatoli Papirovski Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- src/stream_base.cc | 13 +++---------- src/stream_base.h | 1 - src/stream_wrap.cc | 4 ---- src/stream_wrap.h | 1 - 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/stream_base.cc b/src/stream_base.cc index b1aea79d52f762..ecb5f3dd1b954e 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -195,8 +195,7 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { } err = DoWrite(req_wrap, buf_list, count, nullptr); - if (HasWriteQueue()) - req_wrap_obj->Set(env->async(), True(env->isolate())); + req_wrap_obj->Set(env->async(), True(env->isolate())); if (err) req_wrap->Dispose(); @@ -254,8 +253,7 @@ int StreamBase::WriteBuffer(const FunctionCallbackInfo& args) { } err = DoWrite(req_wrap, bufs, count, nullptr); - if (HasWriteQueue()) - req_wrap_obj->Set(env->async(), True(env->isolate())); + req_wrap_obj->Set(env->async(), True(env->isolate())); req_wrap_obj->Set(env->buffer_string(), args[1]); if (err) @@ -381,8 +379,7 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { reinterpret_cast(send_handle)); } - if (HasWriteQueue()) - req_wrap_obj->Set(env->async(), True(env->isolate())); + req_wrap_obj->Set(env->async(), True(env->isolate())); if (err) req_wrap->Dispose(); @@ -476,10 +473,6 @@ int StreamResource::DoTryWrite(uv_buf_t** bufs, size_t* count) { return 0; } -bool StreamResource::HasWriteQueue() { - return true; -} - const char* StreamResource::Error() const { return nullptr; diff --git a/src/stream_base.h b/src/stream_base.h index 071627f3bf2a67..d063176b04a4db 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -162,7 +162,6 @@ class StreamResource { uv_buf_t* bufs, size_t count, uv_stream_t* send_handle) = 0; - virtual bool HasWriteQueue(); virtual const char* Error() const; virtual void ClearError(); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 094991107ba7aa..b639d945004cfa 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -380,10 +380,6 @@ int LibuvStreamWrap::DoWrite(WriteWrap* w, } -bool LibuvStreamWrap::HasWriteQueue() { - return stream()->write_queue_size > 0; -} - void LibuvStreamWrap::AfterUvWrite(uv_write_t* req, int status) { WriteWrap* req_wrap = WriteWrap::from_req(req); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index a695f9a08a7729..0146d41c6e8c7b 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -55,7 +55,6 @@ class LibuvStreamWrap : public HandleWrap, public StreamBase { uv_buf_t* bufs, size_t count, uv_stream_t* send_handle) override; - bool HasWriteQueue() override; inline uv_stream_t* stream() const { return stream_; From c8741ba9d597f66ffcef52d89a4bf34002b39708 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 10 Jan 2018 20:39:05 +0100 Subject: [PATCH 31/47] src: use `DoTryWrite()` for not-all-Buffer writev()s too PR-URL: https://github.com/nodejs/node/pull/18019 Reviewed-By: James M Snell --- src/stream_base.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/stream_base.cc b/src/stream_base.cc index ecb5f3dd1b954e..0fb801ddd57445 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -192,6 +192,13 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { offset += str_size; bytes += str_size; } + + err = DoTryWrite(&buf_list, &count); + if (err != 0 || count == 0) { + req_wrap->Dispatched(); + req_wrap->Dispose(); + goto done; + } } err = DoWrite(req_wrap, buf_list, count, nullptr); From 35b09360ce590699027e45e23c6400fc7caffda4 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 17 Jan 2018 22:55:23 +0100 Subject: [PATCH 32/47] tty: fix console printing on Windows This broke writing non-ASCII data to the console on Windows because the result would be codepage-dependent. This partially reverts 8b751f7eb7b05a0b27f52e2288a636fdd78e9ecb. Fixes: https://github.com/nodejs/node/issues/18189 Refs: https://github.com/nodejs/node/pull/18019 PR-URL: https://github.com/nodejs/node/pull/18214 Fixes: https://github.com/nodejs/node/issues/18189 Refs: https://github.com/nodejs/node/pull/18019 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- lib/tty.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/tty.js b/lib/tty.js index 7b93309efc3d99..9595c79db39a33 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -24,7 +24,6 @@ const util = require('util'); const net = require('net'); const { TTY, isTTY } = process.binding('tty_wrap'); -const { makeSyncWrite } = require('internal/net'); const { inherits } = util; const errnoException = util._errnoException; const errors = require('internal/errors'); @@ -80,8 +79,6 @@ function WriteStream(fd) { // even though it was originally intended to change in v1.0.2 (Libuv 1.2.1). // Ref: https://github.com/nodejs/node/pull/1771#issuecomment-119351671 this._handle.setBlocking(true); - this._writev = null; - this._write = makeSyncWrite(fd); var winSize = new Array(2); var err = this._handle.getWindowSize(winSize); From a3aebea6c0e97583bd5010dd8b2bb7a4dd170407 Mon Sep 17 00:00:00 2001 From: Bartosz Sosnowski Date: Wed, 7 Feb 2018 10:33:51 +0100 Subject: [PATCH 33/47] test: stdio pipe behavior tests Add two regression tests for stdio over pipes. test-stdio-pipe-access tests if accessing stdio pipe that is being read by another process does not deadlocks Node.js. This was reported in https://github.com/nodejs/node/issues/10836 and was fixed in v8.3.0. The deadlock would happen intermittently, so we run the test 5 times. test-stdio-pipe-redirect tests if redirecting one child process stdin to another process stdout does not crash Node as reported in https://github.com/nodejs/node/issues/17493. It was fixed in https://github.com/nodejs/node/pull/18019. PR-URL: https://github.com/nodejs/node/pull/18614 Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- test/parallel/test-stdio-pipe-access.js | 35 +++++++++++++++++++++ test/parallel/test-stdio-pipe-redirect.js | 38 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 test/parallel/test-stdio-pipe-access.js create mode 100644 test/parallel/test-stdio-pipe-redirect.js diff --git a/test/parallel/test-stdio-pipe-access.js b/test/parallel/test-stdio-pipe-access.js new file mode 100644 index 00000000000000..ef84bb83803b26 --- /dev/null +++ b/test/parallel/test-stdio-pipe-access.js @@ -0,0 +1,35 @@ +'use strict'; +require('../common'); + +// Test if Node handles acessing process.stdin if it is a redirected +// pipe without deadlocking +const { spawn, spawnSync } = require('child_process'); + +const numTries = 5; +const who = process.argv.length <= 2 ? 'runner' : process.argv[2]; + +switch (who) { + case 'runner': + for (let num = 0; num < numTries; ++num) { + spawnSync(process.argv0, + [process.argv[1], 'parent'], + { 'stdio': 'inherit' }); + } + break; + case 'parent': + const middle = spawn(process.argv0, + [process.argv[1], 'middle'], + { 'stdio': 'pipe' }); + middle.stdout.on('data', () => {}); + break; + case 'middle': + spawn(process.argv0, + [process.argv[1], 'bottom'], + { 'stdio': [ process.stdin, + process.stdout, + process.stderr ] }); + break; + case 'bottom': + process.stdin; + break; +} diff --git a/test/parallel/test-stdio-pipe-redirect.js b/test/parallel/test-stdio-pipe-redirect.js new file mode 100644 index 00000000000000..b47f5b9cf44af5 --- /dev/null +++ b/test/parallel/test-stdio-pipe-redirect.js @@ -0,0 +1,38 @@ +'use strict'; +require('../common'); + +// Test if Node handles redirecting one child process stdout to another +// process stdin without crashing. +const spawn = require('child_process').spawn; + +const writeSize = 100; +const totalDots = 10000; + +const who = process.argv.length <= 2 ? 'parent' : process.argv[2]; + +switch (who) { + case 'parent': + const consumer = spawn(process.argv0, [process.argv[1], 'consumer'], { + stdio: ['pipe', 'ignore', 'inherit'], + }); + const producer = spawn(process.argv0, [process.argv[1], 'producer'], { + stdio: ['pipe', consumer.stdin, 'inherit'], + }); + process.stdin.on('data', () => {}); + producer.on('exit', process.exit); + break; + case 'producer': + const buffer = Buffer.alloc(writeSize, '.'); + let written = 0; + const write = () => { + if (written < totalDots) { + written += writeSize; + process.stdout.write(buffer, write); + } + }; + write(); + break; + case 'consumer': + process.stdin.on('data', () => {}); + break; +} From 367241acd8e18b9e649b3b368d601a7540e8fbb7 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 8 Jan 2018 01:14:06 +0100 Subject: [PATCH 34/47] src: refactor stream callbacks and ownership Instead of setting individual callbacks on streams and tracking stream ownership through a boolean `consume_` flag, always have one specific listener object in charge of a stream, and call methods on that object rather than generic C-style callbacks. PR-URL: https://github.com/nodejs/node/pull/18334 Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski Reviewed-By: Matteo Collina --- lib/_http_server.js | 2 +- src/connection_wrap.cc | 1 + src/js_stream.cc | 55 +----- src/node_http2.cc | 208 ++++++++++++----------- src/node_http2.h | 33 ++-- src/node_http_parser.cc | 75 +++----- src/pipe_wrap.cc | 1 + src/process_wrap.cc | 1 + src/stream_base-inl.h | 85 ++++++++- src/stream_base.cc | 49 +++++- src/stream_base.h | 201 ++++++++++++---------- src/stream_wrap.cc | 62 +++---- src/stream_wrap.h | 22 ++- src/tcp_wrap.cc | 1 + src/tls_wrap.cc | 115 +++---------- src/tls_wrap.h | 24 +-- src/tty_wrap.cc | 1 + test/parallel/test-tls-socket-destroy.js | 1 + 18 files changed, 463 insertions(+), 474 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 9541993df53321..111a8525c47d62 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -680,7 +680,7 @@ function onSocketPause() { function unconsume(parser, socket) { if (socket._handle) { if (parser._consumed) - parser.unconsume(socket._handle._externalStream); + parser.unconsume(); parser._consumed = false; socket.removeListener('pause', onSocketPause); socket.removeListener('resume', onSocketResume); diff --git a/src/connection_wrap.cc b/src/connection_wrap.cc index 8de77f361dcde4..a6cf67ceee2477 100644 --- a/src/connection_wrap.cc +++ b/src/connection_wrap.cc @@ -3,6 +3,7 @@ #include "connect_wrap.h" #include "env-inl.h" #include "pipe_wrap.h" +#include "stream_base-inl.h" #include "stream_wrap.h" #include "tcp_wrap.h" #include "util-inl.h" diff --git a/src/js_stream.cc b/src/js_stream.cc index 7d1115f12ac3e2..9e67a2094ded89 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -25,9 +25,6 @@ JSStream::JSStream(Environment* env, Local obj) StreamBase(env) { node::Wrap(obj, this); MakeWeak(this); - - set_alloc_cb({ OnAllocImpl, this }); - set_read_cb({ OnReadImpl, this }); } @@ -35,45 +32,6 @@ JSStream::~JSStream() { } -void JSStream::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) { - buf->base = Malloc(size); - buf->len = size; -} - - -void JSStream::OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - JSStream* wrap = static_cast(ctx); - CHECK_NE(wrap, nullptr); - Environment* env = wrap->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - if (nread < 0) { - if (buf != nullptr && buf->base != nullptr) - free(buf->base); - wrap->EmitData(nread, Local(), Local()); - return; - } - - if (nread == 0) { - if (buf->base != nullptr) - free(buf->base); - return; - } - - CHECK_LE(static_cast(nread), buf->len); - char* base = node::Realloc(buf->base, nread); - - CHECK_EQ(pending, UV_UNKNOWN_HANDLE); - - Local obj = Buffer::New(env, base, nread).ToLocalChecked(); - wrap->EmitData(nread, obj, Local()); -} - - AsyncWrap* JSStream::GetAsyncWrap() { return static_cast(this); } @@ -212,18 +170,19 @@ void JSStream::ReadBuffer(const FunctionCallbackInfo& args) { char* data = Buffer::Data(args[0]); int len = Buffer::Length(args[0]); - do { - uv_buf_t buf; + // Repeatedly ask the stream's owner for memory, copy the data that we + // just read from JS into those buffers and emit them as reads. + while (len != 0) { + uv_buf_t buf = wrap->EmitAlloc(len); ssize_t avail = len; - wrap->EmitAlloc(len, &buf); if (static_cast(buf.len) < avail) avail = buf.len; memcpy(buf.base, data, avail); data += avail; len -= avail; - wrap->EmitRead(avail, &buf); - } while (len != 0); + wrap->EmitRead(avail, buf); + } } @@ -231,7 +190,7 @@ void JSStream::EmitEOF(const FunctionCallbackInfo& args) { JSStream* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); - wrap->EmitRead(UV_EOF, nullptr); + wrap->EmitRead(UV_EOF); } diff --git a/src/node_http2.cc b/src/node_http2.cc index bd7eeee8655e52..bd2e93a13c208b 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -531,24 +531,12 @@ Http2Session::Http2Session(Environment* env, outgoing_buffers_.reserve(32); } -void Http2Session::Unconsume() { - if (stream_ != nullptr) { - DEBUG_HTTP2SESSION(this, "unconsuming the i/o stream"); - stream_->set_destruct_cb({ nullptr, nullptr }); - stream_->set_alloc_cb({ nullptr, nullptr }); - stream_->set_read_cb({ nullptr, nullptr }); - stream_->Unconsume(); - stream_ = nullptr; - } -} - Http2Session::~Http2Session() { CHECK_EQ(flags_ & SESSION_STATE_HAS_SCOPE, 0); if (!object().IsEmpty()) ClearWrap(object()); persistent().Reset(); CHECK(persistent().IsEmpty()); - Unconsume(); DEBUG_HTTP2SESSION(this, "freeing nghttp2 session"); nghttp2_session_del(session_); } @@ -646,7 +634,8 @@ void Http2Session::Close(uint32_t code, bool socket_closed) { DEBUG_HTTP2SESSION2(this, "terminating session with code %d", code); CHECK_EQ(nghttp2_session_terminate_session(session_, code), 0); } else { - Unconsume(); + if (stream_ != nullptr) + stream_->RemoveStreamListener(this); } // If there are outstanding pings, those will need to be canceled, do @@ -1044,22 +1033,38 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle, stream->statistics_.received_bytes += len; - // There is a single large array buffer for the entire data read from the - // network; create a slice of that array buffer and emit it as the - // received data buffer. - CHECK(!session->stream_buf_ab_.IsEmpty()); - size_t offset = reinterpret_cast(data) - session->stream_buf_; - // Verify that the data offset is inside the current read buffer. - CHECK_LE(offset, session->stream_buf_size_); - - Local buf = - Buffer::New(env, session->stream_buf_ab_, offset, len).ToLocalChecked(); - - stream->EmitData(len, buf, Local()); - if (!stream->IsReading()) - stream->inbound_consumed_data_while_paused_ += len; - else - nghttp2_session_consume_stream(handle, id, len); + // Repeatedly ask the stream's owner for memory, and copy the read data + // into those buffers. + // The typical case is actually the exception here; Http2StreamListeners + // know about the HTTP2 session associated with this stream, so they know + // about the larger from-socket read buffer, so they do not require copying. + do { + uv_buf_t buf = stream->EmitAlloc(len); + ssize_t avail = len; + if (static_cast(buf.len) < avail) + avail = buf.len; + + // `buf.base == nullptr` is the default Http2StreamListener's way + // of saying that it wants a pointer to the raw original. + // Since it has access to the original socket buffer from which the data + // was read in the first place, it can use that to minizime ArrayBuffer + // allocations. + if (LIKELY(buf.base == nullptr)) + buf.base = reinterpret_cast(const_cast(data)); + else + memcpy(buf.base, data, avail); + data += avail; + len -= avail; + stream->EmitRead(avail, buf); + + // If the stream owner (e.g. the JS Http2Stream) wants more data, just + // tell nghttp2 that all data has been consumed. Otherwise, defer until + // more data is being requested. + if (stream->IsReading()) + nghttp2_session_consume_stream(handle, id, avail); + else + stream->inbound_consumed_data_while_paused_ += avail; + } while (len != 0); } return 0; } @@ -1129,6 +1134,38 @@ inline void Http2Session::GetTrailers(Http2Stream* stream, uint32_t* flags) { } } +uv_buf_t Http2StreamListener::OnStreamAlloc(size_t size) { + // See the comments in Http2Session::OnDataChunkReceived + // (which is the only possible call site for this method). + return uv_buf_init(nullptr, size); +} + +void Http2StreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { + Http2Stream* stream = static_cast(stream_); + Http2Session* session = stream->session(); + Environment* env = stream->env(); + + if (nread < 0) { + PassReadErrorToPreviousListener(nread); + return; + } + + CHECK(!session->stream_buf_ab_.IsEmpty()); + + // There is a single large array buffer for the entire data read from the + // network; create a slice of that array buffer and emit it as the + // received data buffer. + size_t offset = buf.base - session->stream_buf_.base; + + // Verify that the data offset is inside the current read buffer. + CHECK_LE(offset, session->stream_buf_.len); + CHECK_LE(offset + buf.len, session->stream_buf_.len); + + Local buffer = + Buffer::New(env, session->stream_buf_ab_, offset, nread).ToLocalChecked(); + + stream->CallJSOnreadMethod(nread, buffer); +} Http2Stream::SubmitTrailers::SubmitTrailers( Http2Session* session, @@ -1257,7 +1294,7 @@ inline void Http2Session::HandleDataFrame(const nghttp2_frame* frame) { return; if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - stream->EmitData(UV_EOF, Local(), Local()); + stream->EmitRead(UV_EOF); } } @@ -1378,16 +1415,15 @@ inline void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { } // Callback used when data has been written to the stream. -void Http2Session::OnStreamAfterWriteImpl(WriteWrap* w, int status, void* ctx) { - Http2Session* session = static_cast(ctx); - DEBUG_HTTP2SESSION2(session, "write finished with status %d", status); +void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) { + DEBUG_HTTP2SESSION2(this, "write finished with status %d", status); // Inform all pending writes about their completion. - session->ClearOutgoing(status); + ClearOutgoing(status); - if (!(session->flags_ & SESSION_STATE_WRITE_SCHEDULED)) { + if (!(flags_ & SESSION_STATE_WRITE_SCHEDULED)) { // Schedule a new write if nghttp2 wants to send data. - session->MaybeScheduleWrite(); + MaybeScheduleWrite(); } } @@ -1625,97 +1661,76 @@ WriteWrap* Http2Session::AllocateSend() { Local obj = env()->write_wrap_constructor_function() ->NewInstance(env()->context()).ToLocalChecked(); - return WriteWrap::New(env(), obj, stream_); -} - -// Allocates the data buffer used to receive inbound data from the i/o stream -void Http2Session::OnStreamAllocImpl(size_t suggested_size, - uv_buf_t* buf, - void* ctx) { - Http2Session* session = static_cast(ctx); - CHECK_EQ(session->stream_buf_, nullptr); - CHECK_EQ(session->stream_buf_size_, 0); - buf->base = session->stream_buf_ = Malloc(suggested_size); - buf->len = session->stream_buf_size_ = suggested_size; - session->IncrementCurrentSessionMemory(suggested_size); + return WriteWrap::New(env(), obj, static_cast(stream_)); } // Callback used to receive inbound data from the i/o stream -void Http2Session::OnStreamReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - Http2Session* session = static_cast(ctx); - Http2Scope h2scope(session); - CHECK_NE(session->stream_, nullptr); - DEBUG_HTTP2SESSION2(session, "receiving %d bytes", nread); +void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { + Http2Scope h2scope(this); + CHECK_NE(stream_, nullptr); + DEBUG_HTTP2SESSION2(this, "receiving %d bytes", nread); + IncrementCurrentSessionMemory(buf.len); + CHECK(stream_buf_ab_.IsEmpty()); + if (nread <= 0) { - free(session->stream_buf_); + free(buf.base); if (nread < 0) { - uv_buf_t tmp_buf = uv_buf_init(nullptr, 0); - session->prev_read_cb_.fn(nread, - &tmp_buf, - pending, - session->prev_read_cb_.ctx); + PassReadErrorToPreviousListener(nread); } } else { // Only pass data on if nread > 0 + // Makre sure that there was no read previously active. + CHECK_EQ(stream_buf_.base, nullptr); + CHECK_EQ(stream_buf_.len, 0); + + // Remember the current buffer, so that OnDataChunkReceived knows the + // offset of a DATA frame's data into the socket read buffer. + stream_buf_ = uv_buf_init(buf.base, nread); + // Verify that currently: There is memory allocated into which // the data has been read, and that memory buffer is at least as large // as the amount of data we have read, but we have not yet made an // ArrayBuffer out of it. - CHECK_NE(session->stream_buf_, nullptr); - CHECK_EQ(session->stream_buf_, buf->base); - CHECK_EQ(session->stream_buf_size_, buf->len); - CHECK_GE(session->stream_buf_size_, static_cast(nread)); - CHECK(session->stream_buf_ab_.IsEmpty()); + CHECK_LE(static_cast(nread), stream_buf_.len); - Environment* env = session->env(); - Isolate* isolate = env->isolate(); + Isolate* isolate = env()->isolate(); HandleScope scope(isolate); - Local context = env->context(); - Context::Scope context_scope(context); + Context::Scope context_scope(env()->context()); // Create an array buffer for the read data. DATA frames will be emitted // as slices of this array buffer to avoid having to copy memory. - session->stream_buf_ab_ = + stream_buf_ab_ = ArrayBuffer::New(isolate, - session->stream_buf_, - session->stream_buf_size_, + buf.base, + nread, v8::ArrayBufferCreationMode::kInternalized); - uv_buf_t buf_ = uv_buf_init(buf->base, nread); - session->statistics_.data_received += nread; - ssize_t ret = session->Write(&buf_, 1); + statistics_.data_received += nread; + ssize_t ret = Write(&stream_buf_, 1); // Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef // ssize_t to int. Cast here so that the < 0 check actually works on // Windows. if (static_cast(ret) < 0) { - DEBUG_HTTP2SESSION2(session, "fatal error receiving data: %d", ret); + DEBUG_HTTP2SESSION2(this, "fatal error receiving data: %d", ret); - Local argv[1] = { + Local argv[] = { Integer::New(isolate, ret), }; - session->MakeCallback(env->error_string(), arraysize(argv), argv); + MakeCallback(env()->error_string(), arraysize(argv), argv); } else { - DEBUG_HTTP2SESSION2(session, "processed %d bytes. wants more? %d", ret, - nghttp2_session_want_read(**session)); + DEBUG_HTTP2SESSION2(this, "processed %d bytes. wants more? %d", ret, + nghttp2_session_want_read(session_)); } } // Since we are finished handling this write, reset the stream buffer. // The memory has either been free()d or was handed over to V8. - session->DecrementCurrentSessionMemory(session->stream_buf_size_); - session->stream_buf_ = nullptr; - session->stream_buf_size_ = 0; - session->stream_buf_ab_ = Local(); -} + DecrementCurrentSessionMemory(buf.len); -void Http2Session::OnStreamDestructImpl(void* ctx) { - Http2Session* session = static_cast(ctx); - session->stream_ = nullptr; + stream_buf_ab_ = Local(); + stream_buf_ = uv_buf_init(nullptr, 0); } // Every Http2Session session is tightly bound to a single i/o StreamBase @@ -1724,14 +1739,7 @@ void Http2Session::OnStreamDestructImpl(void* ctx) { // C++ layer via the StreamBase API. void Http2Session::Consume(Local external) { StreamBase* stream = static_cast(external->Value()); - stream->Consume(); - stream_ = stream; - prev_alloc_cb_ = stream->alloc_cb(); - prev_read_cb_ = stream->read_cb(); - stream->set_alloc_cb({ Http2Session::OnStreamAllocImpl, this }); - stream->set_read_cb({ Http2Session::OnStreamReadImpl, this }); - stream->set_after_write_cb({ Http2Session::OnStreamAfterWriteImpl, this }); - stream->set_destruct_cb({ Http2Session::OnStreamDestructImpl, this }); + stream->PushStreamListener(this); DEBUG_HTTP2SESSION(this, "i/o stream consumed"); } @@ -1769,6 +1777,8 @@ Http2Stream::Http2Stream( if (options & STREAM_OPTION_GET_TRAILERS) flags_ |= NGHTTP2_STREAM_FLAG_TRAILERS; + PushStreamListener(&stream_listener_); + if (options & STREAM_OPTION_EMPTY_PAYLOAD) Shutdown(); session->AddStream(this); diff --git a/src/node_http2.h b/src/node_http2.h index 9ca5c5c06e8a61..b22539f5119919 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -539,6 +539,12 @@ class Http2Priority { nghttp2_priority_spec spec; }; +class Http2StreamListener : public StreamListener { + public: + uv_buf_t OnStreamAlloc(size_t suggested_size) override; + void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; +}; + class Http2Stream : public AsyncWrap, public StreamBase { public: @@ -751,6 +757,8 @@ class Http2Stream : public AsyncWrap, int64_t fd_offset_ = 0; int64_t fd_length_ = -1; + Http2StreamListener stream_listener_; + friend class Http2Session; }; @@ -802,7 +810,7 @@ class Http2Stream::Provider::Stream : public Http2Stream::Provider { }; -class Http2Session : public AsyncWrap { +class Http2Session : public AsyncWrap, public StreamListener { public: Http2Session(Environment* env, Local wrap, @@ -876,21 +884,11 @@ class Http2Session : public AsyncWrap { size_t self_size() const override { return sizeof(*this); } - char* stream_alloc() { - return stream_buf_; - } - inline void GetTrailers(Http2Stream* stream, uint32_t* flags); - static void OnStreamAllocImpl(size_t suggested_size, - uv_buf_t* buf, - void* ctx); - static void OnStreamReadImpl(ssize_t nread, - const uv_buf_t* bufs, - uv_handle_type pending, - void* ctx); - static void OnStreamAfterWriteImpl(WriteWrap* w, int status, void* ctx); - static void OnStreamDestructImpl(void* ctx); + // Handle reads/writes from the underlying network transport. + void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; + void OnStreamAfterWrite(WriteWrap* w, int status) override; // The JavaScript API static void New(const FunctionCallbackInfo& args); @@ -1078,16 +1076,12 @@ class Http2Session : public AsyncWrap { int flags_ = SESSION_STATE_NONE; // The StreamBase instance being used for i/o - StreamBase* stream_; - StreamResource::Callback prev_alloc_cb_; - StreamResource::Callback prev_read_cb_; padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; // use this to allow timeout tracking during long-lasting writes uint32_t chunks_sent_since_last_write_ = 0; - char* stream_buf_ = nullptr; - size_t stream_buf_size_ = 0; + uv_buf_t stream_buf_ = uv_buf_init(nullptr, 0); v8::Local stream_buf_ab_; size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS; @@ -1103,6 +1097,7 @@ class Http2Session : public AsyncWrap { void ClearOutgoing(int status); friend class Http2Scope; + friend class Http2StreamListener; }; class Http2SessionPerformanceEntry : public PerformanceEntry { diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 9debb8a205ef1c..d4044f8bbeea7b 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -144,7 +144,7 @@ struct StringPtr { }; -class Parser : public AsyncWrap { +class Parser : public AsyncWrap, public StreamListener { public: Parser(Environment* env, Local wrap, enum http_parser_type type) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER), @@ -494,14 +494,7 @@ class Parser : public AsyncWrap { Local stream_obj = args[0].As(); StreamBase* stream = static_cast(stream_obj->Value()); CHECK_NE(stream, nullptr); - - stream->Consume(); - - parser->prev_alloc_cb_ = stream->alloc_cb(); - parser->prev_read_cb_ = stream->read_cb(); - - stream->set_alloc_cb({ OnAllocImpl, parser }); - stream->set_read_cb({ OnReadImpl, parser }); + stream->PushStreamListener(parser); } @@ -510,22 +503,10 @@ class Parser : public AsyncWrap { ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder()); // Already unconsumed - if (parser->prev_alloc_cb_.is_empty()) + if (parser->stream_ == nullptr) return; - // Restore stream's callbacks - if (args.Length() == 1 && args[0]->IsExternal()) { - Local stream_obj = args[0].As(); - StreamBase* stream = static_cast(stream_obj->Value()); - CHECK_NE(stream, nullptr); - - stream->set_alloc_cb(parser->prev_alloc_cb_); - stream->set_read_cb(parser->prev_read_cb_); - stream->Unconsume(); - } - - parser->prev_alloc_cb_.clear(); - parser->prev_read_cb_.clear(); + parser->stream_->RemoveStreamListener(parser); } @@ -544,33 +525,19 @@ class Parser : public AsyncWrap { protected: static const size_t kAllocBufferSize = 64 * 1024; - static void OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) { - Parser* parser = static_cast(ctx); - Environment* env = parser->env(); + uv_buf_t OnStreamAlloc(size_t suggested_size) override { + if (env()->http_parser_buffer() == nullptr) + env()->set_http_parser_buffer(new char[kAllocBufferSize]); - if (env->http_parser_buffer() == nullptr) - env->set_http_parser_buffer(new char[kAllocBufferSize]); - - buf->base = env->http_parser_buffer(); - buf->len = kAllocBufferSize; + return uv_buf_init(env()->http_parser_buffer(), kAllocBufferSize); } - static void OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - Parser* parser = static_cast(ctx); - HandleScope scope(parser->env()->isolate()); + void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override { + HandleScope scope(env()->isolate()); if (nread < 0) { - uv_buf_t tmp_buf; - tmp_buf.base = nullptr; - tmp_buf.len = 0; - parser->prev_read_cb_.fn(nread, - &tmp_buf, - pending, - parser->prev_read_cb_.ctx); + PassReadErrorToPreviousListener(nread); return; } @@ -578,27 +545,27 @@ class Parser : public AsyncWrap { if (nread == 0) return; - parser->current_buffer_.Clear(); - Local ret = parser->Execute(buf->base, nread); + current_buffer_.Clear(); + Local ret = Execute(buf.base, nread); // Exception if (ret.IsEmpty()) return; - Local obj = parser->object(); - Local cb = obj->Get(kOnExecute); + Local cb = + object()->Get(env()->context(), kOnExecute).ToLocalChecked(); if (!cb->IsFunction()) return; // Hooks for GetCurrentBuffer - parser->current_buffer_len_ = nread; - parser->current_buffer_data_ = buf->base; + current_buffer_len_ = nread; + current_buffer_data_ = buf.base; - parser->MakeCallback(cb.As(), 1, &ret); + MakeCallback(cb.As(), 1, &ret); - parser->current_buffer_len_ = 0; - parser->current_buffer_data_ = nullptr; + current_buffer_len_ = 0; + current_buffer_data_ = nullptr; } @@ -713,8 +680,6 @@ class Parser : public AsyncWrap { Local current_buffer_; size_t current_buffer_len_; char* current_buffer_data_; - StreamResource::Callback prev_alloc_cb_; - StreamResource::Callback prev_read_cb_; // These are helper functions for filling `http_parser_settings`, which turn // a member function of Parser into a C-style HTTP parser callback. diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index c5958a2271a83e..016ce480b6a809 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -29,6 +29,7 @@ #include "node_buffer.h" #include "node_wrap.h" #include "connect_wrap.h" +#include "stream_base-inl.h" #include "stream_wrap.h" #include "util-inl.h" diff --git a/src/process_wrap.cc b/src/process_wrap.cc index b01ef56270767e..314131e1dd319f 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -22,6 +22,7 @@ #include "env-inl.h" #include "handle_wrap.h" #include "node_wrap.h" +#include "stream_base-inl.h" #include "util-inl.h" #include diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index cdcff67cc55e66..287978a87034eb 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -25,6 +25,87 @@ using v8::Value; using AsyncHooks = Environment::AsyncHooks; + +inline StreamListener::~StreamListener() { + if (stream_ != nullptr) + stream_->RemoveStreamListener(this); +} + +inline void StreamListener::PassReadErrorToPreviousListener(ssize_t nread) { + CHECK_NE(previous_listener_, nullptr); + previous_listener_->OnStreamRead(nread, + uv_buf_init(nullptr, 0), + UV_UNKNOWN_HANDLE); +} + + +inline StreamResource::~StreamResource() { + while (listener_ != nullptr) { + listener_->OnStreamDestroy(); + RemoveStreamListener(listener_); + } +} + +inline void StreamResource::PushStreamListener(StreamListener* listener) { + CHECK_NE(listener, nullptr); + CHECK_EQ(listener->stream_, nullptr); + + listener->previous_listener_ = listener_; + listener->stream_ = this; + + listener_ = listener; +} + +inline void StreamResource::RemoveStreamListener(StreamListener* listener) { + CHECK_NE(listener, nullptr); + + StreamListener* previous; + StreamListener* current; + + // Remove from the linked list. + for (current = listener_, previous = nullptr; + /* No loop condition because we want a crash if listener is not found */ + ; previous = current, current = current->previous_listener_) { + CHECK_NE(current, nullptr); + if (current == listener) { + if (previous != nullptr) + previous->previous_listener_ = current->previous_listener_; + else + listener_ = listener->previous_listener_; + break; + } + } + + listener->stream_ = nullptr; + listener->previous_listener_ = nullptr; +} + + +inline uv_buf_t StreamResource::EmitAlloc(size_t suggested_size) { + return listener_->OnStreamAlloc(suggested_size); +} + +inline void StreamResource::EmitRead(ssize_t nread, + const uv_buf_t& buf, + uv_handle_type pending) { + if (nread > 0) + bytes_read_ += static_cast(nread); + listener_->OnStreamRead(nread, buf, pending); +} + +inline void StreamResource::EmitAfterWrite(WriteWrap* w, int status) { + listener_->OnStreamAfterWrite(w, status); +} + + +inline StreamBase::StreamBase(Environment* env) : env_(env) { + PushStreamListener(&default_listener_); +} + +inline Environment* StreamBase::stream_env() const { + return env_; +} + template void StreamBase::AddMethods(Environment* env, Local t, @@ -70,8 +151,8 @@ void StreamBase::AddMethods(Environment* env, Local(), attributes); - env->SetProtoMethod(t, "readStart", JSMethod); - env->SetProtoMethod(t, "readStop", JSMethod); + env->SetProtoMethod(t, "readStart", JSMethod); + env->SetProtoMethod(t, "readStop", JSMethod); if ((flags & kFlagNoShutdown) == 0) env->SetProtoMethod(t, "shutdown", JSMethod); if ((flags & kFlagHasWritev) != 0) diff --git a/src/stream_base.cc b/src/stream_base.cc index 0fb801ddd57445..9acf2273abd78b 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -34,12 +34,12 @@ template int StreamBase::WriteString( const FunctionCallbackInfo& args); -int StreamBase::ReadStart(const FunctionCallbackInfo& args) { +int StreamBase::ReadStartJS(const FunctionCallbackInfo& args) { return ReadStart(); } -int StreamBase::ReadStop(const FunctionCallbackInfo& args) { +int StreamBase::ReadStopJS(const FunctionCallbackInfo& args) { return ReadStop(); } @@ -437,9 +437,9 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { } -void StreamBase::EmitData(ssize_t nread, - Local buf, - Local handle) { +void StreamBase::CallJSOnreadMethod(ssize_t nread, + Local buf, + Local handle) { Environment* env = env_; Local argv[] = { @@ -490,4 +490,43 @@ void StreamResource::ClearError() { // No-op } + +uv_buf_t StreamListener::OnStreamAlloc(size_t suggested_size) { + return uv_buf_init(Malloc(suggested_size), suggested_size); +} + +void StreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { + // This cannot be virtual because it is just as valid to override the other + // OnStreamRead() callback. + CHECK(0 && "OnStreamRead() needs to be implemented"); +} + +void StreamListener::OnStreamRead(ssize_t nread, + const uv_buf_t& buf, + uv_handle_type pending) { + CHECK_EQ(pending, UV_UNKNOWN_HANDLE); + OnStreamRead(nread, buf); +} + + +void EmitToJSStreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { + CHECK_NE(stream_, nullptr); + StreamBase* stream = static_cast(stream_); + Environment* env = stream->stream_env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + if (nread <= 0) { + free(buf.base); + if (nread < 0) + stream->CallJSOnreadMethod(nread, Local()); + return; + } + + CHECK_LE(static_cast(nread), buf.len); + + Local obj = Buffer::New(env, buf.base, nread).ToLocalChecked(); + stream->CallJSOnreadMethod(nread, obj); +} + } // namespace node diff --git a/src/stream_base.h b/src/stream_base.h index d063176b04a4db..0b176d11819fca 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -15,6 +15,7 @@ namespace node { // Forward declarations class StreamBase; +class StreamResource; template class StreamReq { @@ -123,38 +124,78 @@ class WriteWrap : public ReqWrap, const size_t storage_size_; }; -class StreamResource { + +// This is the generic interface for objects that control Node.js' C++ streams. +// For example, the default `EmitToJSStreamListener` emits a stream's data +// as Buffers in JS, or `TLSWrap` reads and decrypts data from a stream. +class StreamListener { public: - template - struct Callback { - Callback() : fn(nullptr), ctx(nullptr) {} - Callback(T fn, void* ctx) : fn(fn), ctx(ctx) {} - Callback(const Callback&) = default; - - inline bool is_empty() { return fn == nullptr; } - inline void clear() { - fn = nullptr; - ctx = nullptr; - } + virtual ~StreamListener(); + + // This is called when a stream wants to allocate memory immediately before + // reading data into the freshly allocated buffer (i.e. it is always followed + // by a `OnStreamRead()` call). + // This memory may be statically or dynamically allocated; for example, + // a protocol parser may want to read data into a static buffer if it knows + // that all data is going to be fully handled during the next + // `OnStreamRead()` call. + // The returned buffer does not need to contain `suggested_size` bytes. + // The default implementation of this method returns a buffer that has exactly + // the suggested size and is allocated using malloc(). + virtual uv_buf_t OnStreamAlloc(size_t suggested_size); + + // `OnStreamRead()` is called when data is available on the socket and has + // been read into the buffer provided by `OnStreamAlloc()`. + // The `buf` argument is the return value of `uv_buf_t`, or may be a buffer + // with base nullpptr in case of an error. + // `nread` is the number of read bytes (which is at most the buffer length), + // or, if negative, a libuv error code. + // The variant with a `uv_handle_type` argument is used by libuv-backed + // streams for handle transfers (e.g. passing net.Socket instances between + // cluster workers). For all other streams, overriding the simple variant + // should be sufficient. + // By default, the second variant crashes if `pending` is set and otherwise + // calls the simple variant. + virtual void OnStreamRead(ssize_t nread, + const uv_buf_t& buf) = 0; + virtual void OnStreamRead(ssize_t nread, + const uv_buf_t& buf, + uv_handle_type pending); + + // This is called once a Write has finished. `status` may be 0 or, + // if negative, a libuv error code. + virtual void OnStreamAfterWrite(WriteWrap* w, int status) {} + + // This is called immediately before the stream is destroyed. + virtual void OnStreamDestroy() {} - T fn; - void* ctx; - }; + protected: + // Pass along a read error to the `StreamListener` instance that was active + // before this one. For example, a protocol parser does not care about read + // errors and may instead want to let the original handler + // (e.g. the JS handler) take care of the situation. + void PassReadErrorToPreviousListener(ssize_t nread); - typedef void (*AfterWriteCb)(WriteWrap* w, int status, void* ctx); - typedef void (*AllocCb)(size_t size, uv_buf_t* buf, void* ctx); - typedef void (*ReadCb)(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx); - typedef void (*DestructCb)(void* ctx); + StreamResource* stream_ = nullptr; + StreamListener* previous_listener_ = nullptr; - StreamResource() : bytes_read_(0) { - } - virtual ~StreamResource() { - if (!destruct_cb_.is_empty()) - destruct_cb_.fn(destruct_cb_.ctx); - } + friend class StreamResource; +}; + + +// A default emitter that just pushes data chunks as Buffer instances to +// JS land via the handleā€™s .ondata method. +class EmitToJSStreamListener : public StreamListener { + public: + void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; +}; + + +// A generic stream, comparable to JS landā€™s `Duplex` streams. +// A stream is always controlled through one `StreamListener` instance. +class StreamResource { + public: + virtual ~StreamResource(); virtual int DoShutdown(ShutdownWrap* req_wrap) = 0; virtual int DoTryWrite(uv_buf_t** bufs, size_t* count); @@ -162,50 +203,45 @@ class StreamResource { uv_buf_t* bufs, size_t count, uv_stream_t* send_handle) = 0; - virtual const char* Error() const; - virtual void ClearError(); - - // Events - inline void EmitAfterWrite(WriteWrap* w, int status) { - if (!after_write_cb_.is_empty()) - after_write_cb_.fn(w, status, after_write_cb_.ctx); - } - inline void EmitAlloc(size_t size, uv_buf_t* buf) { - if (!alloc_cb_.is_empty()) - alloc_cb_.fn(size, buf, alloc_cb_.ctx); - } - - inline void EmitRead(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending = UV_UNKNOWN_HANDLE) { - if (nread > 0) - bytes_read_ += static_cast(nread); - if (!read_cb_.is_empty()) - read_cb_.fn(nread, buf, pending, read_cb_.ctx); - } - - inline void set_after_write_cb(Callback c) { - after_write_cb_ = c; - } + // Start reading from the underlying resource. This is called by the consumer + // when more data is desired. + virtual int ReadStart() = 0; + // Stop reading from the underlying resource. This is called by the + // consumer when its buffers are full and no more data can be handled. + virtual int ReadStop() = 0; - inline void set_alloc_cb(Callback c) { alloc_cb_ = c; } - inline void set_read_cb(Callback c) { read_cb_ = c; } - inline void set_destruct_cb(Callback c) { destruct_cb_ = c; } + // Optionally, this may provide an error message to be used for + // failing writes. + virtual const char* Error() const; + // Clear the current error (i.e. that would be returned by Error()). + virtual void ClearError(); - inline Callback after_write_cb() { return after_write_cb_; } - inline Callback alloc_cb() { return alloc_cb_; } - inline Callback read_cb() { return read_cb_; } - inline Callback destruct_cb() { return destruct_cb_; } + // Transfer ownership of this tream to `listener`. The previous listener + // will not receive any more callbacks while the new listener was active. + void PushStreamListener(StreamListener* listener); + // Remove a listener, and, if this was the currently active one, + // transfer ownership back to the previous listener. + void RemoveStreamListener(StreamListener* listener); protected: - Callback after_write_cb_; - Callback alloc_cb_; - Callback read_cb_; - Callback destruct_cb_; - uint64_t bytes_read_; + // Call the current listener's OnStreamAlloc() method. + uv_buf_t EmitAlloc(size_t suggested_size); + // Call the current listener's OnStreamRead() method and update the + // stream's read byte counter. + void EmitRead(ssize_t nread, + const uv_buf_t& buf = uv_buf_init(nullptr, 0), + uv_handle_type pending = UV_UNKNOWN_HANDLE); + // Call the current listener's OnStreamAfterWrite() method. + void EmitAfterWrite(WriteWrap* w, int status); + + StreamListener* listener_ = nullptr; + uint64_t bytes_read_ = 0; + + friend class StreamListener; }; + class StreamBase : public StreamResource { public: enum Flags { @@ -224,40 +260,29 @@ class StreamBase : public StreamResource { virtual bool IsIPCPipe(); virtual int GetFD(); - virtual int ReadStart() = 0; - virtual int ReadStop() = 0; - - inline void Consume() { - CHECK_EQ(consumed_, false); - consumed_ = true; - } - - inline void Unconsume() { - CHECK_EQ(consumed_, true); - consumed_ = false; - } - - void EmitData(ssize_t nread, - v8::Local buf, - v8::Local handle); + void CallJSOnreadMethod( + ssize_t nread, + v8::Local buf, + v8::Local handle = v8::Local()); // These are called by the respective {Write,Shutdown}Wrap class. virtual void AfterShutdown(ShutdownWrap* req, int status); virtual void AfterWrite(WriteWrap* req, int status); - protected: - explicit StreamBase(Environment* env) : env_(env), consumed_(false) { - } + // This is named `stream_env` to avoid name clashes, because a lot of + // subclasses are also `BaseObject`s. + Environment* stream_env() const; - virtual ~StreamBase() = default; + protected: + explicit StreamBase(Environment* env); // One of these must be implemented virtual AsyncWrap* GetAsyncWrap() = 0; virtual v8::Local GetObject(); // JS Methods - int ReadStart(const v8::FunctionCallbackInfo& args); - int ReadStop(const v8::FunctionCallbackInfo& args); + int ReadStartJS(const v8::FunctionCallbackInfo& args); + int ReadStopJS(const v8::FunctionCallbackInfo& args); int Shutdown(const v8::FunctionCallbackInfo& args); int Writev(const v8::FunctionCallbackInfo& args); int WriteBuffer(const v8::FunctionCallbackInfo& args); @@ -280,7 +305,7 @@ class StreamBase : public StreamResource { private: Environment* env_; - bool consumed_; + EmitToJSStreamListener default_listener_; }; } // namespace node diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index b639d945004cfa..0be73f9114adb1 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -93,8 +93,7 @@ LibuvStreamWrap::LibuvStreamWrap(Environment* env, provider), StreamBase(env), stream_(stream) { - set_alloc_cb({ OnAllocImpl, this }); - set_read_cb({ OnReadImpl, this }); + PushStreamListener(this); } @@ -157,23 +156,18 @@ int LibuvStreamWrap::ReadStop() { void LibuvStreamWrap::OnAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) { + size_t suggested_size, + uv_buf_t* buf) { LibuvStreamWrap* wrap = static_cast(handle->data); HandleScope scope(wrap->env()->isolate()); Context::Scope context_scope(wrap->env()->context()); CHECK_EQ(wrap->stream(), reinterpret_cast(handle)); - return wrap->EmitAlloc(suggested_size, buf); + *buf = wrap->EmitAlloc(suggested_size); } -void LibuvStreamWrap::OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx) { - buf->base = node::Malloc(size); - buf->len = size; -} - template static Local AcceptHandle(Environment* env, LibuvStreamWrap* parent) { @@ -196,51 +190,41 @@ static Local AcceptHandle(Environment* env, LibuvStreamWrap* parent) { } -void LibuvStreamWrap::OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - LibuvStreamWrap* wrap = static_cast(ctx); - Environment* env = wrap->env(); - HandleScope handle_scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local pending_obj; +void LibuvStreamWrap::OnStreamRead(ssize_t nread, + const uv_buf_t& buf, + uv_handle_type pending) { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); - if (nread < 0) { - if (buf->base != nullptr) - free(buf->base); - wrap->EmitData(nread, Local(), pending_obj); + if (nread <= 0) { + free(buf.base); + if (nread < 0) + CallJSOnreadMethod(nread, Local()); return; } - if (nread == 0) { - if (buf->base != nullptr) - free(buf->base); - return; - } + CHECK_LE(static_cast(nread), buf.len); - CHECK_LE(static_cast(nread), buf->len); - char* base = node::Realloc(buf->base, nread); + Local pending_obj; if (pending == UV_TCP) { - pending_obj = AcceptHandle(env, wrap); + pending_obj = AcceptHandle(env(), this); } else if (pending == UV_NAMED_PIPE) { - pending_obj = AcceptHandle(env, wrap); + pending_obj = AcceptHandle(env(), this); } else if (pending == UV_UDP) { - pending_obj = AcceptHandle(env, wrap); + pending_obj = AcceptHandle(env(), this); } else { CHECK_EQ(pending, UV_UNKNOWN_HANDLE); } - Local obj = Buffer::New(env, base, nread).ToLocalChecked(); - wrap->EmitData(nread, obj, pending_obj); + Local obj = Buffer::New(env(), buf.base, nread).ToLocalChecked(); + CallJSOnreadMethod(nread, obj, pending_obj); } void LibuvStreamWrap::OnRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf) { + ssize_t nread, + const uv_buf_t* buf) { LibuvStreamWrap* wrap = static_cast(handle->data); HandleScope scope(wrap->env()->isolate()); Context::Scope context_scope(wrap->env()->context()); @@ -263,7 +247,7 @@ void LibuvStreamWrap::OnRead(uv_stream_t* handle, } } - wrap->EmitRead(nread, buf, type); + wrap->EmitRead(nread, *buf, type); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 0146d41c6e8c7b..129006b1600c6c 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -33,7 +33,9 @@ namespace node { -class LibuvStreamWrap : public HandleWrap, public StreamBase { +class LibuvStreamWrap : public HandleWrap, + public StreamListener, + public StreamBase { public: static void Initialize(v8::Local target, v8::Local unused, @@ -79,9 +81,6 @@ class LibuvStreamWrap : public HandleWrap, public StreamBase { uv_stream_t* stream, AsyncWrap::ProviderType provider); - ~LibuvStreamWrap() { - } - AsyncWrap* GetAsyncWrap() override; static void AddMethods(Environment* env, @@ -105,11 +104,16 @@ class LibuvStreamWrap : public HandleWrap, public StreamBase { static void AfterUvShutdown(uv_shutdown_t* req, int status); // Resource interface implementation - static void OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx); - static void OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx); + void OnStreamRead(ssize_t nread, + const uv_buf_t& buf) override { + CHECK(0 && "must not be called"); + } + void OnStreamRead(ssize_t nread, + const uv_buf_t& buf, + uv_handle_type pending) override; + void OnStreamAfterWrite(WriteWrap* w, int status) override { + previous_listener_->OnStreamAfterWrite(w, status); + } void AfterWrite(WriteWrap* req_wrap, int status) override; diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 3a0a3f295e2c72..a0a58fb1b5cc8d 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -27,6 +27,7 @@ #include "node_buffer.h" #include "node_wrap.h" #include "connect_wrap.h" +#include "stream_base-inl.h" #include "stream_wrap.h" #include "util-inl.h" diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 47362a47de6ddb..a38aa7a4c3484a 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -59,7 +59,6 @@ TLSWrap::TLSWrap(Environment* env, SSLWrap(env, sc, kind), StreamBase(env), sc_(sc), - stream_(stream), enc_in_(nullptr), enc_out_(nullptr), write_size_(0), @@ -78,14 +77,7 @@ TLSWrap::TLSWrap(Environment* env, SSL_CTX_sess_set_get_cb(sc_->ctx_, SSLWrap::GetSessionCallback); SSL_CTX_sess_set_new_cb(sc_->ctx_, SSLWrap::NewSessionCallback); - stream_->Consume(); - stream_->set_after_write_cb({ OnAfterWriteImpl, this }); - stream_->set_alloc_cb({ OnAllocImpl, this }); - stream_->set_read_cb({ OnReadImpl, this }); - stream_->set_destruct_cb({ OnDestructImpl, this }); - - set_alloc_cb({ OnAllocSelf, this }); - set_read_cb({ OnReadSelf, this }); + stream->PushStreamListener(this); InitSSL(); } @@ -100,19 +92,6 @@ TLSWrap::~TLSWrap() { #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB sni_context_.Reset(); #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB - - // See test/parallel/test-tls-transport-destroy-after-own-gc.js: - // If this TLSWrap is garbage collected, we cannot allow callbacks to be - // called on this stream. - - if (stream_ == nullptr) - return; - stream_->set_destruct_cb({ nullptr, nullptr }); - stream_->set_after_write_cb({ nullptr, nullptr }); - stream_->set_alloc_cb({ nullptr, nullptr }); - stream_->set_read_cb({ nullptr, nullptr }); - stream_->set_destruct_cb({ nullptr, nullptr }); - stream_->Unconsume(); } @@ -214,15 +193,13 @@ void TLSWrap::Receive(const FunctionCallbackInfo& args) { char* data = Buffer::Data(args[0]); size_t len = Buffer::Length(args[0]); - uv_buf_t buf; - // Copy given buffer entirely or partiall if handle becomes closed while (len > 0 && wrap->IsAlive() && !wrap->IsClosing()) { - wrap->stream_->EmitAlloc(len, &buf); + uv_buf_t buf = wrap->OnStreamAlloc(len); size_t copy = buf.len > len ? len : buf.len; memcpy(buf.base, data, copy); buf.len = copy; - wrap->stream_->EmitRead(buf.len, &buf); + wrap->OnStreamRead(copy, buf); data += copy; len -= copy; @@ -315,7 +292,7 @@ void TLSWrap::EncOut() { ->NewInstance(env()->context()).ToLocalChecked(); WriteWrap* write_req = WriteWrap::New(env(), req_wrap_obj, - stream_); + static_cast(stream_)); uv_buf_t buf[arraysize(data)]; for (size_t i = 0; i < count; i++) @@ -332,7 +309,7 @@ void TLSWrap::EncOut() { } -void TLSWrap::EncOutAfterWrite(WriteWrap* req_wrap, int status) { +void TLSWrap::OnStreamAfterWrite(WriteWrap* req_wrap, int status) { // We should not be getting here after `DestroySSL`, because all queued writes // must be invoked with UV_ECANCELED CHECK_NE(ssl_, nullptr); @@ -429,12 +406,11 @@ void TLSWrap::ClearOut() { while (read > 0) { int avail = read; - uv_buf_t buf; - EmitAlloc(avail, &buf); + uv_buf_t buf = EmitAlloc(avail); if (static_cast(buf.len) < avail) avail = buf.len; memcpy(buf.base, current, avail); - EmitRead(avail, &buf); + EmitRead(avail, buf); // Caveat emptor: OnRead() calls into JS land which can result in // the SSL context object being destroyed. We have to carefully @@ -450,7 +426,7 @@ void TLSWrap::ClearOut() { int flags = SSL_get_shutdown(ssl_); if (!eof_ && flags & SSL_RECEIVED_SHUTDOWN) { eof_ = true; - EmitRead(UV_EOF, nullptr); + EmitRead(UV_EOF); } // We need to check whether an error occurred or the connection was @@ -532,22 +508,24 @@ AsyncWrap* TLSWrap::GetAsyncWrap() { bool TLSWrap::IsIPCPipe() { - return stream_->IsIPCPipe(); + return static_cast(stream_)->IsIPCPipe(); } int TLSWrap::GetFD() { - return stream_->GetFD(); + return static_cast(stream_)->GetFD(); } bool TLSWrap::IsAlive() { - return ssl_ != nullptr && stream_ != nullptr && stream_->IsAlive(); + return ssl_ != nullptr && + stream_ != nullptr && + static_cast(stream_)->IsAlive(); } bool TLSWrap::IsClosing() { - return stream_->IsClosing(); + return static_cast(stream_)->IsClosing(); } @@ -646,62 +624,16 @@ int TLSWrap::DoWrite(WriteWrap* w, } -void TLSWrap::OnAfterWriteImpl(WriteWrap* w, int status, void* ctx) { - TLSWrap* wrap = static_cast(ctx); - wrap->EncOutAfterWrite(w, status); -} - - -void TLSWrap::OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) { - TLSWrap* wrap = static_cast(ctx); - - if (wrap->ssl_ == nullptr) { - *buf = uv_buf_init(nullptr, 0); - return; - } - - size_t size = 0; - buf->base = crypto::NodeBIO::FromBIO(wrap->enc_in_)->PeekWritable(&size); - buf->len = size; -} - - -void TLSWrap::OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - TLSWrap* wrap = static_cast(ctx); - wrap->DoRead(nread, buf, pending); -} - - -void TLSWrap::OnDestructImpl(void* ctx) { - TLSWrap* wrap = static_cast(ctx); - wrap->clear_stream(); -} - - -void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) { - buf->base = node::Malloc(suggested_size); - buf->len = suggested_size; -} - +uv_buf_t TLSWrap::OnStreamAlloc(size_t suggested_size) { + CHECK_NE(ssl_, nullptr); -void TLSWrap::OnReadSelf(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx) { - TLSWrap* wrap = static_cast(ctx); - Local buf_obj; - if (buf != nullptr) - buf_obj = Buffer::New(wrap->env(), buf->base, buf->len).ToLocalChecked(); - wrap->EmitData(nread, buf_obj, Local()); + size_t size = suggested_size; + char* base = crypto::NodeBIO::FromBIO(enc_in_)->PeekWritable(&size); + return uv_buf_init(base, size); } -void TLSWrap::DoRead(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending) { +void TLSWrap::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { if (nread < 0) { // Error should be emitted only after all data was read ClearOut(); @@ -713,13 +645,13 @@ void TLSWrap::DoRead(ssize_t nread, eof_ = true; } - EmitRead(nread, nullptr); + EmitRead(nread); return; } // Only client connections can receive data if (ssl_ == nullptr) { - EmitRead(UV_EPROTO, nullptr); + EmitRead(UV_EPROTO); return; } @@ -814,6 +746,9 @@ void TLSWrap::DestroySSL(const FunctionCallbackInfo& args) { // Destroy the SSL structure and friends wrap->SSLWrap::DestroySSL(); + + if (wrap->stream_ != nullptr) + wrap->stream_->RemoveStreamListener(wrap); } diff --git a/src/tls_wrap.h b/src/tls_wrap.h index ae83c82c3226fd..a1f0b99e86beec 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -48,7 +48,8 @@ class NodeBIO; class TLSWrap : public AsyncWrap, public crypto::SSLWrap, - public StreamBase { + public StreamBase, + public StreamListener { public: ~TLSWrap() override; @@ -76,8 +77,6 @@ class TLSWrap : public AsyncWrap, size_t self_size() const override { return sizeof(*this); } - void clear_stream() { stream_ = nullptr; } - protected: static const int kClearOutChunkSize = 16384; @@ -98,7 +97,6 @@ class TLSWrap : public AsyncWrap, static void SSLInfoCallback(const SSL* ssl_, int where, int ret); void InitSSL(); void EncOut(); - void EncOutAfterWrite(WriteWrap* req_wrap, int status); bool ClearIn(); void ClearOut(); bool InvokeQueued(int status, const char* error_str = nullptr); @@ -119,20 +117,9 @@ class TLSWrap : public AsyncWrap, bool IsIPCPipe() override; // Resource implementation - static void OnAfterWriteImpl(WriteWrap* w, int status, void* ctx); - static void OnAllocImpl(size_t size, uv_buf_t* buf, void* ctx); - static void OnReadImpl(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx); - static void OnAllocSelf(size_t size, uv_buf_t* buf, void* ctx); - static void OnReadSelf(ssize_t nread, - const uv_buf_t* buf, - uv_handle_type pending, - void* ctx); - static void OnDestructImpl(void* ctx); - - void DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); + void OnStreamAfterWrite(WriteWrap* w, int status) override; + uv_buf_t OnStreamAlloc(size_t size) override; + void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; v8::Local GetSSLError(int status, int* err, std::string* msg); @@ -154,7 +141,6 @@ class TLSWrap : public AsyncWrap, #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB crypto::SecureContext* sc_; - StreamBase* stream_; BIO* enc_in_; BIO* enc_out_; std::vector pending_cleartext_input_; diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 111c568cb52a8a..d12f0b956348f5 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -26,6 +26,7 @@ #include "node_buffer.h" #include "node_wrap.h" #include "req_wrap-inl.h" +#include "stream_base-inl.h" #include "stream_wrap.h" #include "util-inl.h" diff --git a/test/parallel/test-tls-socket-destroy.js b/test/parallel/test-tls-socket-destroy.js index f62b6f905296db..6f1d4b4186b74f 100644 --- a/test/parallel/test-tls-socket-destroy.js +++ b/test/parallel/test-tls-socket-destroy.js @@ -19,6 +19,7 @@ const server = net.createServer(common.mustCall((conn) => { const socket = new tls.TLSSocket(conn, options); socket.once('data', common.mustCall(() => { socket._destroySSL(); // Should not crash. + socket.destroy(); server.close(); })); })); From 6a709330231c031079555c0ed9c6048d5d460fb4 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 25 Jan 2018 00:43:06 +0100 Subject: [PATCH 35/47] src: simplify handles for libuv streams Instead of passing along the handle object, just set it as a property on the stream handle object and let the read handler grab it from there. PR-URL: https://github.com/nodejs/node/pull/18334 Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski Reviewed-By: Matteo Collina --- lib/internal/child_process.js | 5 +- src/env.h | 1 + src/stream_base-inl.h | 10 ++-- src/stream_base.cc | 23 +------- src/stream_base.h | 18 +------ src/stream_wrap.cc | 98 ++++++++++++++--------------------- src/stream_wrap.h | 26 ++-------- 7 files changed, 53 insertions(+), 128 deletions(-) diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 13d0a11aa1e705..44904658217d77 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -455,7 +455,10 @@ function setupChannel(target, channel) { var jsonBuffer = ''; var pendingHandle = null; channel.buffering = false; - channel.onread = function(nread, pool, recvHandle) { + channel.pendingHandle = null; + channel.onread = function(nread, pool) { + const recvHandle = channel.pendingHandle; + channel.pendingHandle = null; // TODO(bnoordhuis) Check that nread > 0. if (pool) { if (recvHandle) diff --git a/src/env.h b/src/env.h index 5ebd56a3fff633..89027767f3885a 100644 --- a/src/env.h +++ b/src/env.h @@ -215,6 +215,7 @@ class ModuleWrap; V(owner_string, "owner") \ V(parse_error_string, "Parse Error") \ V(path_string, "path") \ + V(pending_handle_string, "pendingHandle") \ V(pbkdf2_error_string, "PBKDF2 Error") \ V(pid_string, "pid") \ V(pipe_string, "pipe") \ diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 287978a87034eb..76922c1d8af77d 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -33,9 +33,7 @@ inline StreamListener::~StreamListener() { inline void StreamListener::PassReadErrorToPreviousListener(ssize_t nread) { CHECK_NE(previous_listener_, nullptr); - previous_listener_->OnStreamRead(nread, - uv_buf_init(nullptr, 0), - UV_UNKNOWN_HANDLE); + previous_listener_->OnStreamRead(nread, uv_buf_init(nullptr, 0)); } @@ -85,12 +83,10 @@ inline uv_buf_t StreamResource::EmitAlloc(size_t suggested_size) { return listener_->OnStreamAlloc(suggested_size); } -inline void StreamResource::EmitRead(ssize_t nread, - const uv_buf_t& buf, - uv_handle_type pending) { +inline void StreamResource::EmitRead(ssize_t nread, const uv_buf_t& buf) { if (nread > 0) bytes_read_ += static_cast(nread); - listener_->OnStreamRead(nread, buf, pending); + listener_->OnStreamRead(nread, buf); } inline void StreamResource::EmitAfterWrite(WriteWrap* w, int status) { diff --git a/src/stream_base.cc b/src/stream_base.cc index 9acf2273abd78b..8bdcebe88ab19f 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -437,23 +437,17 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) { } -void StreamBase::CallJSOnreadMethod(ssize_t nread, - Local buf, - Local handle) { +void StreamBase::CallJSOnreadMethod(ssize_t nread, Local buf) { Environment* env = env_; Local argv[] = { Integer::New(env->isolate(), nread), - buf, - handle + buf }; if (argv[1].IsEmpty()) argv[1] = Undefined(env->isolate()); - if (argv[2].IsEmpty()) - argv[2] = Undefined(env->isolate()); - AsyncWrap* wrap = GetAsyncWrap(); CHECK_NE(wrap, nullptr); wrap->MakeCallback(env->onread_string(), arraysize(argv), argv); @@ -495,19 +489,6 @@ uv_buf_t StreamListener::OnStreamAlloc(size_t suggested_size) { return uv_buf_init(Malloc(suggested_size), suggested_size); } -void StreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { - // This cannot be virtual because it is just as valid to override the other - // OnStreamRead() callback. - CHECK(0 && "OnStreamRead() needs to be implemented"); -} - -void StreamListener::OnStreamRead(ssize_t nread, - const uv_buf_t& buf, - uv_handle_type pending) { - CHECK_EQ(pending, UV_UNKNOWN_HANDLE); - OnStreamRead(nread, buf); -} - void EmitToJSStreamListener::OnStreamRead(ssize_t nread, const uv_buf_t& buf) { CHECK_NE(stream_, nullptr); diff --git a/src/stream_base.h b/src/stream_base.h index 0b176d11819fca..f18b6bda0a08a4 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -150,17 +150,8 @@ class StreamListener { // with base nullpptr in case of an error. // `nread` is the number of read bytes (which is at most the buffer length), // or, if negative, a libuv error code. - // The variant with a `uv_handle_type` argument is used by libuv-backed - // streams for handle transfers (e.g. passing net.Socket instances between - // cluster workers). For all other streams, overriding the simple variant - // should be sufficient. - // By default, the second variant crashes if `pending` is set and otherwise - // calls the simple variant. virtual void OnStreamRead(ssize_t nread, const uv_buf_t& buf) = 0; - virtual void OnStreamRead(ssize_t nread, - const uv_buf_t& buf, - uv_handle_type pending); // This is called once a Write has finished. `status` may be 0 or, // if negative, a libuv error code. @@ -229,9 +220,7 @@ class StreamResource { uv_buf_t EmitAlloc(size_t suggested_size); // Call the current listener's OnStreamRead() method and update the // stream's read byte counter. - void EmitRead(ssize_t nread, - const uv_buf_t& buf = uv_buf_init(nullptr, 0), - uv_handle_type pending = UV_UNKNOWN_HANDLE); + void EmitRead(ssize_t nread, const uv_buf_t& buf = uv_buf_init(nullptr, 0)); // Call the current listener's OnStreamAfterWrite() method. void EmitAfterWrite(WriteWrap* w, int status); @@ -260,10 +249,7 @@ class StreamBase : public StreamResource { virtual bool IsIPCPipe(); virtual int GetFD(); - void CallJSOnreadMethod( - ssize_t nread, - v8::Local buf, - v8::Local handle = v8::Local()); + void CallJSOnreadMethod(ssize_t nread, v8::Local buf); // These are called by the respective {Write,Shutdown}Wrap class. virtual void AfterShutdown(ShutdownWrap* req, int status); diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 0be73f9114adb1..bc10cf80e828f1 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -93,7 +93,6 @@ LibuvStreamWrap::LibuvStreamWrap(Environment* env, provider), StreamBase(env), stream_(stream) { - PushStreamListener(this); } @@ -146,7 +145,13 @@ bool LibuvStreamWrap::IsIPCPipe() { int LibuvStreamWrap::ReadStart() { - return uv_read_start(stream(), OnAlloc, OnRead); + return uv_read_start(stream(), [](uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static_cast(handle->data)->OnUvAlloc(suggested_size, buf); + }, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + static_cast(stream->data)->OnUvRead(nread, buf); + }); } @@ -155,16 +160,11 @@ int LibuvStreamWrap::ReadStop() { } -void LibuvStreamWrap::OnAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) { - LibuvStreamWrap* wrap = static_cast(handle->data); - HandleScope scope(wrap->env()->isolate()); - Context::Scope context_scope(wrap->env()->context()); - - CHECK_EQ(wrap->stream(), reinterpret_cast(handle)); +void LibuvStreamWrap::OnUvAlloc(size_t suggested_size, uv_buf_t* buf) { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); - *buf = wrap->EmitAlloc(suggested_size); + *buf = EmitAlloc(suggested_size); } @@ -190,64 +190,47 @@ static Local AcceptHandle(Environment* env, LibuvStreamWrap* parent) { } -void LibuvStreamWrap::OnStreamRead(ssize_t nread, - const uv_buf_t& buf, - uv_handle_type pending) { - HandleScope handle_scope(env()->isolate()); +void LibuvStreamWrap::OnUvRead(ssize_t nread, const uv_buf_t* buf) { + HandleScope scope(env()->isolate()); Context::Scope context_scope(env()->context()); - - if (nread <= 0) { - free(buf.base); - if (nread < 0) - CallJSOnreadMethod(nread, Local()); - return; - } - - CHECK_LE(static_cast(nread), buf.len); - - Local pending_obj; - - if (pending == UV_TCP) { - pending_obj = AcceptHandle(env(), this); - } else if (pending == UV_NAMED_PIPE) { - pending_obj = AcceptHandle(env(), this); - } else if (pending == UV_UDP) { - pending_obj = AcceptHandle(env(), this); - } else { - CHECK_EQ(pending, UV_UNKNOWN_HANDLE); - } - - Local obj = Buffer::New(env(), buf.base, nread).ToLocalChecked(); - CallJSOnreadMethod(nread, obj, pending_obj); -} - - -void LibuvStreamWrap::OnRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf) { - LibuvStreamWrap* wrap = static_cast(handle->data); - HandleScope scope(wrap->env()->isolate()); - Context::Scope context_scope(wrap->env()->context()); uv_handle_type type = UV_UNKNOWN_HANDLE; - if (wrap->is_named_pipe_ipc() && - uv_pipe_pending_count(reinterpret_cast(handle)) > 0) { - type = uv_pipe_pending_type(reinterpret_cast(handle)); + if (is_named_pipe_ipc() && + uv_pipe_pending_count(reinterpret_cast(stream())) > 0) { + type = uv_pipe_pending_type(reinterpret_cast(stream())); } // We should not be getting this callback if someone as already called // uv_close() on the handle. - CHECK_EQ(wrap->persistent().IsEmpty(), false); + CHECK_EQ(persistent().IsEmpty(), false); if (nread > 0) { - if (wrap->is_tcp()) { + if (is_tcp()) { NODE_COUNT_NET_BYTES_RECV(nread); - } else if (wrap->is_named_pipe()) { + } else if (is_named_pipe()) { NODE_COUNT_PIPE_BYTES_RECV(nread); } + + Local pending_obj; + + if (type == UV_TCP) { + pending_obj = AcceptHandle(env(), this); + } else if (type == UV_NAMED_PIPE) { + pending_obj = AcceptHandle(env(), this); + } else if (type == UV_UDP) { + pending_obj = AcceptHandle(env(), this); + } else { + CHECK_EQ(type, UV_UNKNOWN_HANDLE); + } + + if (!pending_obj.IsEmpty()) { + object()->Set(env()->context(), + env()->pending_handle_string(), + pending_obj).FromJust(); + } } - wrap->EmitRead(nread, *buf, type); + EmitRead(nread, *buf); } @@ -373,11 +356,6 @@ void LibuvStreamWrap::AfterUvWrite(uv_write_t* req, int status) { req_wrap->Done(status); } - -void LibuvStreamWrap::AfterWrite(WriteWrap* w, int status) { - StreamBase::AfterWrite(w, status); -} - } // namespace node NODE_BUILTIN_MODULE_CONTEXT_AWARE(stream_wrap, diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 129006b1600c6c..e5ad25b91e6fea 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -33,9 +33,7 @@ namespace node { -class LibuvStreamWrap : public HandleWrap, - public StreamListener, - public StreamBase { +class LibuvStreamWrap : public HandleWrap, public StreamBase { public: static void Initialize(v8::Local target, v8::Local unused, @@ -93,30 +91,12 @@ class LibuvStreamWrap : public HandleWrap, static void SetBlocking(const v8::FunctionCallbackInfo& args); // Callbacks for libuv - static void OnAlloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf); + void OnUvAlloc(size_t suggested_size, uv_buf_t* buf); + void OnUvRead(ssize_t nread, const uv_buf_t* buf); - static void OnRead(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf); static void AfterUvWrite(uv_write_t* req, int status); static void AfterUvShutdown(uv_shutdown_t* req, int status); - // Resource interface implementation - void OnStreamRead(ssize_t nread, - const uv_buf_t& buf) override { - CHECK(0 && "must not be called"); - } - void OnStreamRead(ssize_t nread, - const uv_buf_t& buf, - uv_handle_type pending) override; - void OnStreamAfterWrite(WriteWrap* w, int status) override { - previous_listener_->OnStreamAfterWrite(w, status); - } - - void AfterWrite(WriteWrap* req_wrap, int status) override; - uv_stream_t* const stream_; }; From 3e1d8103a68a64b7368fe19e8eb2570f6d58dffe Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 3 Feb 2018 18:02:57 +0100 Subject: [PATCH 36/47] test: introduce SetUpTestCase/TearDownTestCase This commit add SetUpTestCase and TearDownTestCase functions that will be called once per test case. Currently we only have SetUp/TearDown which are called for each test. This commit moves the initialization and configuration of Node and V8 to be done on a per test case basis, but gives each test a new Isolate. Backport-PR-URL: https://github.com/nodejs/node/pull/18957 PR-URL: https://github.com/nodejs/node/pull/18558 Reviewed-By: Ben Noordhuis --- test/cctest/node_test_fixture.cc | 5 ++++- test/cctest/node_test_fixture.h | 37 ++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/test/cctest/node_test_fixture.cc b/test/cctest/node_test_fixture.cc index 477adb5711af38..e4675c5db3c9f9 100644 --- a/test/cctest/node_test_fixture.cc +++ b/test/cctest/node_test_fixture.cc @@ -1,3 +1,6 @@ #include "node_test_fixture.h" -uv_loop_t current_loop; +uv_loop_t NodeTestFixture::current_loop; +std::unique_ptr NodeTestFixture::platform; +std::unique_ptr NodeTestFixture::allocator; +v8::Isolate::CreateParams NodeTestFixture::params; diff --git a/test/cctest/node_test_fixture.h b/test/cctest/node_test_fixture.h index ae9ec32e1ee9e4..5080f0334808eb 100644 --- a/test/cctest/node_test_fixture.h +++ b/test/cctest/node_test_fixture.h @@ -52,42 +52,47 @@ struct Argv { int nr_args_; }; -extern uv_loop_t current_loop; class NodeTestFixture : public ::testing::Test { public: static uv_loop_t* CurrentLoop() { return ¤t_loop; } - node::MultiIsolatePlatform* Platform() const { return platform_; } + node::MultiIsolatePlatform* Platform() const { return platform.get(); } protected: + static std::unique_ptr allocator; + static std::unique_ptr platform; + static v8::Isolate::CreateParams params; + static uv_loop_t current_loop; v8::Isolate* isolate_; - virtual void SetUp() { + static void SetUpTestCase() { + platform.reset(new node::NodePlatform(4, nullptr)); + allocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); + params.array_buffer_allocator = allocator.get(); CHECK_EQ(0, uv_loop_init(¤t_loop)); - platform_ = new node::NodePlatform(8, nullptr); - v8::V8::InitializePlatform(platform_); + v8::V8::InitializePlatform(platform.get()); v8::V8::Initialize(); - v8::Isolate::CreateParams params_; - params_.array_buffer_allocator = allocator_.get(); - isolate_ = v8::Isolate::New(params_); } - virtual void TearDown() { - platform_->Shutdown(); + static void TearDownTestCase() { + platform->Shutdown(); while (uv_loop_alive(¤t_loop)) { uv_run(¤t_loop, UV_RUN_ONCE); } v8::V8::ShutdownPlatform(); - delete platform_; - platform_ = nullptr; CHECK_EQ(0, uv_loop_close(¤t_loop)); } - private: - node::NodePlatform* platform_ = nullptr; - std::unique_ptr allocator_{ - v8::ArrayBuffer::Allocator::NewDefaultAllocator()}; + virtual void SetUp() { + isolate_ = v8::Isolate::New(params); + CHECK_NE(isolate_, nullptr); + } + + virtual void TearDown() { + isolate_->Dispose(); + isolate_ = nullptr; + } }; #endif // TEST_CCTEST_NODE_TEST_FIXTURE_H_ From 337d529e1e942010563d003459806ca36aa094ed Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 21 Nov 2017 19:24:09 +0100 Subject: [PATCH 37/47] lib: add `process` to internal module wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Share `process` through the module wrapper rather than relying on nobody messing with `global.process`. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17198 Fixes: https://github.com/nodejs/node/issues/6802 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Lance Ball Reviewed-By: Anatoli Papirovski Reviewed-By: Joyee Cheung Reviewed-By: Alexey Orlenko Reviewed-By: MichaĆ«l Zasso Reviewed-By: Colin Ihrig Reviewed-By: Timothy Gu Reviewed-By: Refael Ackermann --- lib/internal/bootstrap_node.js | 4 ++-- test/parallel/test-repl-let-process.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-repl-let-process.js diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 85d72f876124fa..b10b8fe54cf1ef 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -634,7 +634,7 @@ }; NativeModule.wrapper = [ - '(function (exports, require, module, internalBinding) {', + '(function (exports, require, module, internalBinding, process) {', '\n});' ]; @@ -650,7 +650,7 @@ lineOffset: 0, displayErrors: true }); - fn(this.exports, NativeModule.require, this, internalBinding); + fn(this.exports, NativeModule.require, this, internalBinding, process); this.loaded = true; } finally { diff --git a/test/parallel/test-repl-let-process.js b/test/parallel/test-repl-let-process.js new file mode 100644 index 00000000000000..3e6c3e85665be1 --- /dev/null +++ b/test/parallel/test-repl-let-process.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const repl = require('repl'); + +common.globalCheck = false; + +// Regression test for https://github.com/nodejs/node/issues/6802 +const input = new common.ArrayStream(); +repl.start({ input, output: process.stdout, useGlobal: true }); +input.run(['let process']); From fc2fc21399cc58e370ba25ce5f2b1a23e1dc3ca5 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sun, 17 Dec 2017 22:45:45 -0500 Subject: [PATCH 38/47] process: refactor nextTick for clarity Do not share unnecessary information about nextTick state between JS & C++, instead only track whether a nextTick is scheduled or not. Turn nextTickQueue into an Object instead of a class since multiple instances are never created. Other assorted refinements and refactoring. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17738 Reviewed-By: Anna Henningsen --- lib/internal/process/next_tick.js | 150 ++++++++++-------------------- src/env-inl.h | 14 +-- src/env.h | 11 +-- src/node.cc | 33 ++++--- 4 files changed, 76 insertions(+), 132 deletions(-) diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index bf7d0bc94dc4ce..2f68783d91800d 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -1,47 +1,9 @@ 'use strict'; -// This value is used to prevent the nextTickQueue from becoming too -// large and cause the process to run out of memory. When this value -// is reached the nextTimeQueue array will be shortened (see tickDone -// for details). -const kMaxCallbacksPerLoop = 1e4; - exports.setup = setupNextTick; // Will be overwritten when setupNextTick() is called. exports.nextTick = null; -class NextTickQueue { - constructor() { - this.head = null; - this.tail = null; - } - - push(v) { - const entry = { data: v, next: null }; - if (this.tail !== null) - this.tail.next = entry; - else - this.head = entry; - this.tail = entry; - } - - shift() { - if (this.head === null) - return; - const ret = this.head.data; - if (this.head === this.tail) - this.head = this.tail = null; - else - this.head = this.head.next; - return ret; - } - - clear() { - this.head = null; - this.tail = null; - } -} - function setupNextTick() { const async_wrap = process.binding('async_wrap'); const async_hooks = require('internal/async_hooks'); @@ -56,15 +18,47 @@ function setupNextTick() { // Grab the constants necessary for working with internal arrays. const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants; const { async_id_symbol, trigger_async_id_symbol } = async_wrap; - const nextTickQueue = new NextTickQueue(); - var microtasksScheduled = false; - // Used to run V8's micro task queue. - var _runMicrotasks = {}; + // tickInfo is used so that the C++ code in src/node.cc can + // have easy access to our nextTick state, and avoid unnecessary + // calls into JS land. + // runMicrotasks is used to run V8's micro task queue. + const [ + tickInfo, + runMicrotasks + ] = process._setupNextTick(_tickCallback); // *Must* match Environment::TickInfo::Fields in src/env.h. - var kIndex = 0; - var kLength = 1; + const kScheduled = 0; + + const nextTickQueue = { + head: null, + tail: null, + push(data) { + const entry = { data, next: null }; + if (this.tail !== null) { + this.tail.next = entry; + } else { + this.head = entry; + tickInfo[kScheduled] = 1; + } + this.tail = entry; + }, + shift() { + if (this.head === null) + return; + const ret = this.head.data; + if (this.head === this.tail) { + this.head = this.tail = null; + tickInfo[kScheduled] = 0; + } else { + this.head = this.head.next; + } + return ret; + } + }; + + var microtasksScheduled = false; process.nextTick = nextTick; // Needs to be accessible from beyond this scope. @@ -73,25 +67,6 @@ function setupNextTick() { // Set the nextTick() function for internal usage. exports.nextTick = internalNextTick; - // This tickInfo thing is used so that the C++ code in src/node.cc - // can have easy access to our nextTick state, and avoid unnecessary - // calls into JS land. - const tickInfo = process._setupNextTick(_tickCallback, _runMicrotasks); - - _runMicrotasks = _runMicrotasks.runMicrotasks; - - function tickDone() { - if (tickInfo[kLength] !== 0) { - if (tickInfo[kLength] <= tickInfo[kIndex]) { - nextTickQueue.clear(); - tickInfo[kLength] = 0; - } else { - tickInfo[kLength] -= tickInfo[kIndex]; - } - } - tickInfo[kIndex] = 0; - } - const microTasksTickObject = { callback: runMicrotasksCallback, args: undefined, @@ -105,38 +80,27 @@ function setupNextTick() { // For the moment all microtasks come from the void until the PromiseHook // API is implemented. nextTickQueue.push(microTasksTickObject); - - tickInfo[kLength]++; microtasksScheduled = true; } function runMicrotasksCallback() { microtasksScheduled = false; - _runMicrotasks(); + runMicrotasks(); - if (tickInfo[kIndex] < tickInfo[kLength] || - emitPendingUnhandledRejections()) { + if (nextTickQueue.head !== null || emitPendingUnhandledRejections()) scheduleMicrotasks(); - } } function _tickCallback() { + let tock; do { - while (tickInfo[kIndex] < tickInfo[kLength]) { - ++tickInfo[kIndex]; - const tock = nextTickQueue.shift(); - - // CHECK(Number.isSafeInteger(tock[async_id_symbol])) - // CHECK(tock[async_id_symbol] > 0) - // CHECK(Number.isSafeInteger(tock[trigger_async_id_symbol])) - // CHECK(tock[trigger_async_id_symbol] > 0) - + while (tock = nextTickQueue.shift()) { const asyncId = tock[async_id_symbol]; emitBefore(asyncId, tock[trigger_async_id_symbol]); // emitDestroy() places the async_id_symbol into an asynchronous queue // that calls the destroy callback in the future. It's called before // calling tock.callback so destroy will be called even if the callback - // throws an exception that is handles by 'uncaughtException' or a + // throws an exception that is handled by 'uncaughtException' or a // domain. // TODO(trevnorris): This is a bit of a hack. It relies on the fact // that nextTick() doesn't allow the event loop to proceed, but if @@ -152,24 +116,21 @@ function setupNextTick() { Reflect.apply(callback, undefined, tock.args); emitAfter(asyncId); - - if (kMaxCallbacksPerLoop < tickInfo[kIndex]) - tickDone(); } - tickDone(); - _runMicrotasks(); + runMicrotasks(); emitPendingUnhandledRejections(); - } while (tickInfo[kLength] !== 0); + } while (nextTickQueue.head !== null); } class TickObject { - constructor(callback, args, asyncId, triggerAsyncId) { + constructor(callback, args, triggerAsyncId) { // this must be set to null first to avoid function tracking // on the hidden class, revisit in V8 versions after 6.2 this.callback = null; this.callback = callback; this.args = args; + const asyncId = ++async_id_fields[kAsyncIdCounter]; this[async_id_symbol] = asyncId; this[trigger_async_id_symbol] = triggerAsyncId; @@ -203,13 +164,7 @@ function setupNextTick() { args[i - 1] = arguments[i]; } - // In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the - // TickObject incurs a significant performance penalty in the - // next-tick-breadth-args benchmark (revisit later) - ++tickInfo[kLength]; - nextTickQueue.push(new TickObject(callback, - args, - ++async_id_fields[kAsyncIdCounter], + nextTickQueue.push(new TickObject(callback, args, getDefaultTriggerAsyncId())); } @@ -238,13 +193,6 @@ function setupNextTick() { if (triggerAsyncId === null) triggerAsyncId = getDefaultTriggerAsyncId(); - // In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the - // TickObject incurs a significant performance penalty in the - // next-tick-breadth-args benchmark (revisit later) - ++tickInfo[kLength]; - nextTickQueue.push(new TickObject(callback, - args, - ++async_id_fields[kAsyncIdCounter], - triggerAsyncId)); + nextTickQueue.push(new TickObject(callback, args, triggerAsyncId)); } } diff --git a/src/env-inl.h b/src/env-inl.h index bf919644dfbe49..476be6763d1248 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -222,7 +222,7 @@ inline Environment::TickInfo::TickInfo() { fields_[i] = 0; } -inline uint32_t* Environment::TickInfo::fields() { +inline uint8_t* Environment::TickInfo::fields() { return fields_; } @@ -230,16 +230,8 @@ inline int Environment::TickInfo::fields_count() const { return kFieldsCount; } -inline uint32_t Environment::TickInfo::index() const { - return fields_[kIndex]; -} - -inline uint32_t Environment::TickInfo::length() const { - return fields_[kLength]; -} - -inline void Environment::TickInfo::set_index(uint32_t value) { - fields_[kIndex] = value; +inline uint8_t Environment::TickInfo::scheduled() const { + return fields_[kScheduled]; } inline void Environment::AssignToContext(v8::Local context, diff --git a/src/env.h b/src/env.h index 89027767f3885a..3b0955f71eddd4 100644 --- a/src/env.h +++ b/src/env.h @@ -453,23 +453,20 @@ class Environment { class TickInfo { public: - inline uint32_t* fields(); + inline uint8_t* fields(); inline int fields_count() const; - inline uint32_t index() const; - inline uint32_t length() const; - inline void set_index(uint32_t value); + inline uint8_t scheduled() const; private: friend class Environment; // So we can call the constructor. inline TickInfo(); enum Fields { - kIndex, - kLength, + kScheduled, kFieldsCount }; - uint32_t fields_[kFieldsCount]; + uint8_t fields_[kFieldsCount]; DISALLOW_COPY_AND_ASSIGN(TickInfo); }; diff --git a/src/node.cc b/src/node.cc index 5fbbe1cf9f824c..bccd7329a9e3ab 100644 --- a/src/node.cc +++ b/src/node.cc @@ -169,6 +169,7 @@ using v8::SealHandleScope; using v8::String; using v8::TryCatch; using v8::Uint32Array; +using v8::Uint8Array; using v8::Undefined; using v8::V8; using v8::Value; @@ -1144,25 +1145,32 @@ void SetupNextTick(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsFunction()); - CHECK(args[1]->IsObject()); env->set_tick_callback_function(args[0].As()); - env->SetMethod(args[1].As(), "runMicrotasks", RunMicrotasks); - - // Do a little housekeeping. env->process_object()->Delete( env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")).FromJust(); + FIXED_ONE_BYTE_STRING(env->isolate(), "_setupNextTick")).FromJust(); // Values use to cross communicate with processNextTick. - uint32_t* const fields = env->tick_info()->fields(); - uint32_t const fields_count = env->tick_info()->fields_count(); + uint8_t* const fields = env->tick_info()->fields(); + uint8_t const fields_count = env->tick_info()->fields_count(); Local array_buffer = ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); + v8::Local run_microtasks_fn = + env->NewFunctionTemplate(RunMicrotasks)->GetFunction(env->context()) + .ToLocalChecked(); + run_microtasks_fn->SetName( + FIXED_ONE_BYTE_STRING(env->isolate(), "runMicrotasks")); + + Local ret = Array::New(env->isolate(), 2); + ret->Set(env->context(), 0, + Uint8Array::New(array_buffer, 0, fields_count)).FromJust(); + ret->Set(env->context(), 1, run_microtasks_fn).FromJust(); + + args.GetReturnValue().Set(ret); } void PromiseRejectCallback(PromiseRejectMessage message) { @@ -1278,7 +1286,7 @@ void InternalCallbackScope::Close() { Environment::TickInfo* tick_info = env_->tick_info(); - if (tick_info->length() == 0) { + if (tick_info->scheduled() == 0) { env_->isolate()->RunMicrotasks(); } @@ -1289,10 +1297,7 @@ void InternalCallbackScope::Close() { CHECK_EQ(env_->trigger_async_id(), 0); } - Local process = env_->process_object(); - - if (tick_info->length() == 0) { - tick_info->set_index(0); + if (tick_info->scheduled() == 0) { return; } @@ -1301,6 +1306,8 @@ void InternalCallbackScope::Close() { CHECK_EQ(env_->trigger_async_id(), 0); } + Local process = env_->process_object(); + if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { failed_ = true; } From 7f9b554500f4466718f15075572c29d5d3fb8f1d Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sat, 23 Dec 2017 10:39:52 -0500 Subject: [PATCH 39/47] process: do not directly schedule _tickCallback in _fatalException When a process encounters a _fatalException that is caught, it should schedule execution of nextTicks but not in an arbitrary place of the next Immediates queue. Instead, add a no-op function to the queue that will ensure processImmediate runs, which will then ensure that nextTicks are processed at the end. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17841 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- lib/internal/bootstrap_node.js | 45 +++++++++---------- .../test-process-fatal-exception-tick.js | 24 ++++++++++ 2 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 test/parallel/test-process-fatal-exception-tick.js diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index b10b8fe54cf1ef..06bed3faaa4870 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -409,6 +409,8 @@ } } + function noop() {} + function setupProcessFatal() { const async_wrap = process.binding('async_wrap'); // Arrays containing hook flags and ids for async_hook calls. @@ -419,23 +421,15 @@ kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants; process._fatalException = function(er) { - var caught; - // It's possible that kDefaultTriggerAsyncId was set for a constructor // call that threw and was never cleared. So clear it now. async_id_fields[kDefaultTriggerAsyncId] = -1; if (exceptionHandlerState.captureFn !== null) { exceptionHandlerState.captureFn(er); - caught = true; - } - - if (!caught) - caught = process.emit('uncaughtException', er); - - // If someone handled it, then great. otherwise, die in C++ land - // since that means that we'll exit the process, emit the 'exit' event - if (!caught) { + } else if (!process.emit('uncaughtException', er)) { + // If someone handled it, then great. otherwise, die in C++ land + // since that means that we'll exit the process, emit the 'exit' event try { if (!process._exiting) { process._exiting = true; @@ -444,24 +438,25 @@ } catch (er) { // nothing to be done about it at this point. } + return false; + } + // If we handled an error, then make sure any ticks get processed + // by ensuring that the next Immediate cycle isn't empty + NativeModule.require('timers').setImmediate(noop); + + // Emit the after() hooks now that the exception has been handled. + if (async_hook_fields[kAfter] > 0) { + const { emitAfter } = NativeModule.require('internal/async_hooks'); + do { + emitAfter(async_id_fields[kExecutionAsyncId]); + } while (async_hook_fields[kStackLength] > 0); + // Or completely empty the id stack. } else { - // If we handled an error, then make sure any ticks get processed - NativeModule.require('timers').setImmediate(process._tickCallback); - - // Emit the after() hooks now that the exception has been handled. - if (async_hook_fields[kAfter] > 0) { - do { - NativeModule.require('internal/async_hooks').emitAfter( - async_id_fields[kExecutionAsyncId]); - } while (async_hook_fields[kStackLength] > 0); - // Or completely empty the id stack. - } else { - clearAsyncIdStack(); - } + clearAsyncIdStack(); } - return caught; + return true; }; } diff --git a/test/parallel/test-process-fatal-exception-tick.js b/test/parallel/test-process-fatal-exception-tick.js new file mode 100644 index 00000000000000..19678d2922516e --- /dev/null +++ b/test/parallel/test-process-fatal-exception-tick.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// If a process encounters an uncaughtException, it should schedule +// processing of nextTicks on the next Immediates cycle but not +// before all Immediates are handled + +let stage = 0; + +process.once('uncaughtException', common.expectsError({ + type: Error, + message: 'caughtException' +})); + +setImmediate(() => { + stage++; + process.nextTick(() => assert.strictEqual(stage, 2)); +}); +const now = Date.now(); +setTimeout(() => setImmediate(() => stage++), 1); +while (now + 10 >= Date.now()); +throw new Error('caughtException'); From 839a3f792d2a8a5f965a7182dff58d23c06524fc Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 18 Dec 2017 13:43:53 +0100 Subject: [PATCH 40/47] timers: make setImmediate() immune to tampering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make setImmediate() immune to `process` global tampering by removing the dependency on the `process._immediateCallback` property. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17736 Fixes: https://github.com/nodejs/node/issues/17681 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Tobias NieƟen Reviewed-By: Anatoli Papirovski Reviewed-By: Jeremiah Senkpiel Reviewed-By: James M Snell --- lib/timers.js | 23 ++++++++++------------- src/env.cc | 2 +- src/env.h | 2 +- src/node.cc | 15 --------------- src/timer_wrap.cc | 23 +++++++++++++++++++++++ test/common/index.js | 1 + test/parallel/test-timer-immediate.js | 5 +++++ 7 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 test/parallel/test-timer-immediate.js diff --git a/lib/timers.js b/lib/timers.js index 43d2cbbd07bdb3..de0c2689c5ccc2 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -22,7 +22,10 @@ 'use strict'; const async_wrap = process.binding('async_wrap'); -const TimerWrap = process.binding('timer_wrap').Timer; +const { + Timer: TimerWrap, + setImmediateCallback, +} = process.binding('timer_wrap'); const L = require('internal/linkedlist'); const internalUtil = require('internal/util'); const { createPromise, promiseResolve } = process.binding('util'); @@ -47,12 +50,8 @@ const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants; const async_id_symbol = Symbol('asyncId'); const trigger_async_id_symbol = Symbol('triggerAsyncId'); -/* This is an Uint32Array for easier sharing with C++ land. */ -const scheduledImmediateCount = process._scheduledImmediateCount; -delete process._scheduledImmediateCount; -/* Kick off setImmediate processing */ -const activateImmediateCheck = process._activateImmediateCheck; -delete process._activateImmediateCheck; +const [activateImmediateCheck, scheduledImmediateCountArray] = + setImmediateCallback(processImmediate); // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2 ** 31 - 1; @@ -706,8 +705,6 @@ function processImmediate() { } } -process._immediateCallback = processImmediate; - // An optimization so that the try/finally only de-optimizes (since at least v8 // 4.7) what is in this smaller function. function tryOnImmediate(immediate, oldTail) { @@ -724,7 +721,7 @@ function tryOnImmediate(immediate, oldTail) { if (!immediate._destroyed) { immediate._destroyed = true; - scheduledImmediateCount[0]--; + scheduledImmediateCountArray[0]--; if (async_hook_fields[kDestroy] > 0) { emitDestroy(immediate[async_id_symbol]); @@ -778,9 +775,9 @@ function Immediate(callback, args) { this); } - if (scheduledImmediateCount[0] === 0) + if (scheduledImmediateCountArray[0] === 0) activateImmediateCheck(); - scheduledImmediateCount[0]++; + scheduledImmediateCountArray[0]++; immediateQueue.append(this); } @@ -826,7 +823,7 @@ exports.clearImmediate = function(immediate) { if (!immediate) return; if (!immediate._destroyed) { - scheduledImmediateCount[0]--; + scheduledImmediateCountArray[0]--; immediate._destroyed = true; if (async_hook_fields[kDestroy] > 0) { diff --git a/src/env.cc b/src/env.cc index 902429e18a7e74..67a6f2c3d46fd3 100644 --- a/src/env.cc +++ b/src/env.cc @@ -309,7 +309,7 @@ void Environment::CheckImmediate(uv_check_t* handle) { MakeCallback(env->isolate(), env->process_object(), - env->immediate_callback_string(), + env->immediate_callback_function(), 0, nullptr, {0, 0}).ToLocalChecked(); diff --git a/src/env.h b/src/env.h index 3b0955f71eddd4..fa6ecb2e6bd69c 100644 --- a/src/env.h +++ b/src/env.h @@ -158,7 +158,6 @@ class ModuleWrap; V(homedir_string, "homedir") \ V(hostmaster_string, "hostmaster") \ V(ignore_string, "ignore") \ - V(immediate_callback_string, "_immediateCallback") \ V(infoaccess_string, "infoAccess") \ V(inherit_string, "inherit") \ V(input_string, "input") \ @@ -289,6 +288,7 @@ class ModuleWrap; V(http2ping_constructor_template, v8::ObjectTemplate) \ V(http2stream_constructor_template, v8::ObjectTemplate) \ V(http2settings_constructor_template, v8::ObjectTemplate) \ + V(immediate_callback_function, v8::Function) \ V(inspector_console_api_object, v8::Object) \ V(pbkdf2_constructor_template, v8::ObjectTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \ diff --git a/src/node.cc b/src/node.cc index bccd7329a9e3ab..34422a373b9c65 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3169,12 +3169,6 @@ static void DebugEnd(const FunctionCallbackInfo& args); namespace { -void ActivateImmediateCheck(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->ActivateImmediateCheck(); -} - - void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); env->StartProfilerIdleNotifier(); @@ -3399,12 +3393,6 @@ void SetupProcessObject(Environment* env, FIXED_ONE_BYTE_STRING(env->isolate(), "ppid"), GetParentProcessId).FromJust()); - auto scheduled_immediate_count = - FIXED_ONE_BYTE_STRING(env->isolate(), "_scheduledImmediateCount"); - CHECK(process->Set(env->context(), - scheduled_immediate_count, - env->scheduled_immediate_count().GetJSArray()).FromJust()); - auto should_abort_on_uncaught_toggle = FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle"); CHECK(process->Set(env->context(), @@ -3536,9 +3524,6 @@ void SetupProcessObject(Environment* env, env->as_external()).FromJust()); // define various internal methods - env->SetMethod(process, - "_activateImmediateCheck", - ActivateImmediateCheck); env->SetMethod(process, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 874c80d8d7095b..5c3f499d163005 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -29,7 +29,9 @@ namespace node { namespace { +using v8::Array; using v8::Context; +using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; @@ -67,11 +69,32 @@ class TimerWrap : public HandleWrap { env->SetProtoMethod(constructor, "stop", Stop); target->Set(timerString, constructor->GetFunction()); + + target->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "setImmediateCallback"), + env->NewFunctionTemplate(SetImmediateCallback) + ->GetFunction(env->context()).ToLocalChecked()).FromJust(); } size_t self_size() const override { return sizeof(*this); } private: + static void SetImmediateCallback(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsFunction()); + auto env = Environment::GetCurrent(args); + env->set_immediate_callback_function(args[0].As()); + auto activate_cb = [] (const FunctionCallbackInfo& args) { + Environment::GetCurrent(args)->ActivateImmediateCheck(); + }; + auto activate_function = + env->NewFunctionTemplate(activate_cb)->GetFunction(env->context()) + .ToLocalChecked(); + auto result = Array::New(env->isolate(), 2); + result->Set(0, activate_function); + result->Set(1, env->scheduled_immediate_count().GetJSArray()); + args.GetReturnValue().Set(result); + } + static void New(const FunctionCallbackInfo& args) { // This constructor should not be exposed to public javascript. // Therefore we assert that we are not trying to call this as a diff --git a/test/common/index.js b/test/common/index.js index ce07d91c80224c..eb4d5a5251cab8 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -21,6 +21,7 @@ /* eslint-disable required-modules, crypto-check */ 'use strict'; +const process = global.process; // Some tests tamper with the process global. const path = require('path'); const fs = require('fs'); const assert = require('assert'); diff --git a/test/parallel/test-timer-immediate.js b/test/parallel/test-timer-immediate.js new file mode 100644 index 00000000000000..385fa4baca4ee5 --- /dev/null +++ b/test/parallel/test-timer-immediate.js @@ -0,0 +1,5 @@ +'use strict'; +const common = require('../common'); +common.globalCheck = false; +global.process = {}; // Boom! +setImmediate(common.mustCall()); From d82efdb74ee84437b3b12132a32e44d8f39c8f28 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Wed, 27 Dec 2017 13:47:51 -0500 Subject: [PATCH 41/47] src: use AliasedBuffer for TickInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17881 Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Khaidi Chu Reviewed-By: Tobias NieƟen --- lib/internal/process/next_tick.js | 6 +++--- src/env-inl.h | 17 ++++++----------- src/env.h | 11 +++++------ src/node.cc | 14 +++----------- 4 files changed, 17 insertions(+), 31 deletions(-) diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index 2f68783d91800d..9481bebd224be6 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -29,7 +29,7 @@ function setupNextTick() { ] = process._setupNextTick(_tickCallback); // *Must* match Environment::TickInfo::Fields in src/env.h. - const kScheduled = 0; + const kHasScheduled = 0; const nextTickQueue = { head: null, @@ -40,7 +40,7 @@ function setupNextTick() { this.tail.next = entry; } else { this.head = entry; - tickInfo[kScheduled] = 1; + tickInfo[kHasScheduled] = 1; } this.tail = entry; }, @@ -50,7 +50,7 @@ function setupNextTick() { const ret = this.head.data; if (this.head === this.tail) { this.head = this.tail = null; - tickInfo[kScheduled] = 0; + tickInfo[kHasScheduled] = 0; } else { this.head = this.head.next; } diff --git a/src/env-inl.h b/src/env-inl.h index 476be6763d1248..a4a27cbfe69ff8 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -217,21 +217,15 @@ inline bool Environment::AsyncCallbackScope::in_makecallback() const { return env_->makecallback_cntr_ > 1; } -inline Environment::TickInfo::TickInfo() { - for (int i = 0; i < kFieldsCount; ++i) - fields_[i] = 0; -} +inline Environment::TickInfo::TickInfo(v8::Isolate* isolate) + : fields_(isolate, kFieldsCount) {} -inline uint8_t* Environment::TickInfo::fields() { +inline AliasedBuffer& Environment::TickInfo::fields() { return fields_; } -inline int Environment::TickInfo::fields_count() const { - return kFieldsCount; -} - -inline uint8_t Environment::TickInfo::scheduled() const { - return fields_[kScheduled]; +inline bool Environment::TickInfo::has_scheduled() const { + return fields_[kHasScheduled] == 1; } inline void Environment::AssignToContext(v8::Local context, @@ -269,6 +263,7 @@ inline Environment::Environment(IsolateData* isolate_data, v8::Local context) : isolate_(context->GetIsolate()), isolate_data_(isolate_data), + tick_info_(context->GetIsolate()), timer_base_(uv_now(isolate_data->event_loop())), printed_error_(false), trace_sync_io_(false), diff --git a/src/env.h b/src/env.h index fa6ecb2e6bd69c..d8012f7bcba762 100644 --- a/src/env.h +++ b/src/env.h @@ -453,20 +453,19 @@ class Environment { class TickInfo { public: - inline uint8_t* fields(); - inline int fields_count() const; - inline uint8_t scheduled() const; + inline AliasedBuffer& fields(); + inline bool has_scheduled() const; private: friend class Environment; // So we can call the constructor. - inline TickInfo(); + inline explicit TickInfo(v8::Isolate* isolate); enum Fields { - kScheduled, + kHasScheduled, kFieldsCount }; - uint8_t fields_[kFieldsCount]; + AliasedBuffer fields_; DISALLOW_COPY_AND_ASSIGN(TickInfo); }; diff --git a/src/node.cc b/src/node.cc index 34422a373b9c65..9e84dda74a9daa 100644 --- a/src/node.cc +++ b/src/node.cc @@ -169,7 +169,6 @@ using v8::SealHandleScope; using v8::String; using v8::TryCatch; using v8::Uint32Array; -using v8::Uint8Array; using v8::Undefined; using v8::V8; using v8::Value; @@ -1152,13 +1151,6 @@ void SetupNextTick(const FunctionCallbackInfo& args) { env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "_setupNextTick")).FromJust(); - // Values use to cross communicate with processNextTick. - uint8_t* const fields = env->tick_info()->fields(); - uint8_t const fields_count = env->tick_info()->fields_count(); - - Local array_buffer = - ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - v8::Local run_microtasks_fn = env->NewFunctionTemplate(RunMicrotasks)->GetFunction(env->context()) .ToLocalChecked(); @@ -1167,7 +1159,7 @@ void SetupNextTick(const FunctionCallbackInfo& args) { Local ret = Array::New(env->isolate(), 2); ret->Set(env->context(), 0, - Uint8Array::New(array_buffer, 0, fields_count)).FromJust(); + env->tick_info()->fields().GetJSArray()).FromJust(); ret->Set(env->context(), 1, run_microtasks_fn).FromJust(); args.GetReturnValue().Set(ret); @@ -1286,7 +1278,7 @@ void InternalCallbackScope::Close() { Environment::TickInfo* tick_info = env_->tick_info(); - if (tick_info->scheduled() == 0) { + if (!tick_info->has_scheduled()) { env_->isolate()->RunMicrotasks(); } @@ -1297,7 +1289,7 @@ void InternalCallbackScope::Close() { CHECK_EQ(env_->trigger_async_id(), 0); } - if (tick_info->scheduled() == 0) { + if (!tick_info->has_scheduled()) { return; } From d2475cc5e6911c254082096eb79a439b7b004eea Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Tue, 26 Dec 2017 14:17:37 -0500 Subject: [PATCH 42/47] timers: refactor setImmediate error handling If an error is encountered during the processing of Immediates, schedule the remaining queue to finish after all error handling code runs (if the process is still alive to do so). The new changes make the Immediates error handling behaviour entirely deterministic and predictable, as the full queue will be flushed on each Immediates cycle, regardless of whether an error is encountered or not. Currently this processing is scheduled for nextTick which can yield unpredictable results as the nextTick might happen as early as close callbacks phase or as late as after the next event loop turns Immediates all fully processed. The latter can result in two full cycles of Immediates processing during one even loop turn. The current implementation also doesn't differentiate between Immediates scheduled for the current queue run or the next one, so Immediates that were scheduled for the next turn of the event loop, will process alongside the ones that were scheduled for the current turn. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/17879 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- lib/timers.js | 65 ++++++++++--------- src/env-inl.h | 39 ++++++++--- src/env.cc | 20 +++--- src/env.h | 29 ++++++++- src/timer_wrap.cc | 5 +- .../test-timers-immediate-queue-throw.js | 53 +++++++++++++++ 6 files changed, 160 insertions(+), 51 deletions(-) create mode 100644 test/parallel/test-timers-immediate-queue-throw.js diff --git a/lib/timers.js b/lib/timers.js index de0c2689c5ccc2..5a9073756fc1fe 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -50,7 +50,11 @@ const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants; const async_id_symbol = Symbol('asyncId'); const trigger_async_id_symbol = Symbol('triggerAsyncId'); -const [activateImmediateCheck, scheduledImmediateCountArray] = +// *Must* match Environment::ImmediateInfo::Fields in src/env.h. +const kCount = 0; +const kHasOutstanding = 1; + +const [activateImmediateCheck, immediateInfo] = setImmediateCallback(processImmediate); // Timeout values > TIMEOUT_MAX are set to 1. @@ -674,16 +678,23 @@ ImmediateList.prototype.remove = function(item) { }; // Create a single linked list instance only once at startup -var immediateQueue = new ImmediateList(); +const immediateQueue = new ImmediateList(); + +// If an uncaught exception was thrown during execution of immediateQueue, +// this queue will store all remaining Immediates that need to run upon +// resolution of all error handling (if process is still alive). +const outstandingQueue = new ImmediateList(); function processImmediate() { - var immediate = immediateQueue.head; - var tail = immediateQueue.tail; + const queue = outstandingQueue.head !== null ? + outstandingQueue : immediateQueue; + var immediate = queue.head; + var tail = queue.tail; // Clear the linked list early in case new `setImmediate()` calls occur while // immediate callbacks are executed - immediateQueue.head = immediateQueue.tail = null; + queue.head = queue.tail = null; while (immediate !== null) { if (!immediate._onImmediate) { @@ -692,9 +703,14 @@ function processImmediate() { } // Save next in case `clearImmediate(immediate)` is called from callback - var next = immediate._idleNext; + const next = immediate._idleNext; + + const asyncId = immediate[async_id_symbol]; + emitBefore(asyncId, immediate[trigger_async_id_symbol]); - tryOnImmediate(immediate, tail); + tryOnImmediate(immediate, next, tail); + + emitAfter(asyncId); // If `clearImmediate(immediate)` wasn't called from the callback, use the // `immediate`'s next item @@ -703,45 +719,36 @@ function processImmediate() { else immediate = next; } + + immediateInfo[kHasOutstanding] = 0; } // An optimization so that the try/finally only de-optimizes (since at least v8 // 4.7) what is in this smaller function. -function tryOnImmediate(immediate, oldTail) { +function tryOnImmediate(immediate, next, oldTail) { var threw = true; - emitBefore(immediate[async_id_symbol], immediate[trigger_async_id_symbol]); try { // make the actual call outside the try/finally to allow it to be optimized runCallback(immediate); threw = false; } finally { immediate._onImmediate = null; - if (!threw) - emitAfter(immediate[async_id_symbol]); if (!immediate._destroyed) { immediate._destroyed = true; - scheduledImmediateCountArray[0]--; + immediateInfo[kCount]--; if (async_hook_fields[kDestroy] > 0) { emitDestroy(immediate[async_id_symbol]); } } - if (threw && immediate._idleNext !== null) { - // Handle any remaining on next tick, assuming we're still alive to do so. - const curHead = immediateQueue.head; - const next = immediate._idleNext; - if (curHead !== null) { - curHead._idlePrev = oldTail; - oldTail._idleNext = curHead; - next._idlePrev = null; - immediateQueue.head = next; - } else { - immediateQueue.head = next; - immediateQueue.tail = oldTail; - } - process.nextTick(processImmediate); + if (threw && (immediate._idleNext !== null || next !== null)) { + // Handle any remaining Immediates after error handling has resolved, + // assuming we're still alive to do so. + outstandingQueue.head = immediate._idleNext || next; + outstandingQueue.tail = oldTail; + immediateInfo[kHasOutstanding] = 1; } } } @@ -775,9 +782,9 @@ function Immediate(callback, args) { this); } - if (scheduledImmediateCountArray[0] === 0) + if (immediateInfo[kCount] === 0) activateImmediateCheck(); - scheduledImmediateCountArray[0]++; + immediateInfo[kCount]++; immediateQueue.append(this); } @@ -823,7 +830,7 @@ exports.clearImmediate = function(immediate) { if (!immediate) return; if (!immediate._destroyed) { - scheduledImmediateCountArray[0]--; + immediateInfo[kCount]--; immediate._destroyed = true; if (async_hook_fields[kDestroy] > 0) { diff --git a/src/env-inl.h b/src/env-inl.h index a4a27cbfe69ff8..0328f058de3ab0 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -217,6 +217,30 @@ inline bool Environment::AsyncCallbackScope::in_makecallback() const { return env_->makecallback_cntr_ > 1; } +inline Environment::ImmediateInfo::ImmediateInfo(v8::Isolate* isolate) + : fields_(isolate, kFieldsCount) {} + +inline AliasedBuffer& + Environment::ImmediateInfo::fields() { + return fields_; +} + +inline uint32_t Environment::ImmediateInfo::count() const { + return fields_[kCount]; +} + +inline bool Environment::ImmediateInfo::has_outstanding() const { + return fields_[kHasOutstanding] == 1; +} + +inline void Environment::ImmediateInfo::count_inc(uint32_t increment) { + fields_[kCount] = fields_[kCount] + increment; +} + +inline void Environment::ImmediateInfo::count_dec(uint32_t decrement) { + fields_[kCount] = fields_[kCount] - decrement; +} + inline Environment::TickInfo::TickInfo(v8::Isolate* isolate) : fields_(isolate, kFieldsCount) {} @@ -263,6 +287,7 @@ inline Environment::Environment(IsolateData* isolate_data, v8::Local context) : isolate_(context->GetIsolate()), isolate_data_(isolate_data), + immediate_info_(context->GetIsolate()), tick_info_(context->GetIsolate()), timer_base_(uv_now(isolate_data->event_loop())), printed_error_(false), @@ -270,7 +295,6 @@ inline Environment::Environment(IsolateData* isolate_data, abort_on_uncaught_exception_(false), emit_napi_warning_(true), makecallback_cntr_(0), - scheduled_immediate_count_(isolate_, 1), should_abort_on_uncaught_toggle_(isolate_, 1), #if HAVE_INSPECTOR inspector_agent_(new inspector::Agent(this)), @@ -357,6 +381,10 @@ inline Environment::AsyncHooks* Environment::async_hooks() { return &async_hooks_; } +inline Environment::ImmediateInfo* Environment::immediate_info() { + return &immediate_info_; +} + inline Environment::TickInfo* Environment::tick_info() { return &tick_info_; } @@ -486,11 +514,6 @@ inline void Environment::set_fs_stats_field_array(double* fields) { fs_stats_field_array_ = fields; } -inline AliasedBuffer& -Environment::scheduled_immediate_count() { - return scheduled_immediate_count_; -} - void Environment::SetImmediate(native_immediate_callback cb, void* data, v8::Local obj) { @@ -500,9 +523,9 @@ void Environment::SetImmediate(native_immediate_callback cb, std::unique_ptr>( obj.IsEmpty() ? nullptr : new v8::Persistent(isolate_, obj)) }); - if (scheduled_immediate_count_[0] == 0) + if (immediate_info()->count() == 0) ActivateImmediateCheck(); - scheduled_immediate_count_[0] = scheduled_immediate_count_[0] + 1; + immediate_info()->count_inc(1); } inline performance::performance_state* Environment::performance_state() { diff --git a/src/env.cc b/src/env.cc index 67a6f2c3d46fd3..e4afb35786e2af 100644 --- a/src/env.cc +++ b/src/env.cc @@ -281,14 +281,14 @@ void Environment::RunAndClearNativeImmediates() { } #ifdef DEBUG - CHECK_GE(scheduled_immediate_count_[0], count); + CHECK_GE(immediate_info()->count(), count); #endif - scheduled_immediate_count_[0] = scheduled_immediate_count_[0] - count; + immediate_info()->count_dec(count); } } static bool MaybeStopImmediate(Environment* env) { - if (env->scheduled_immediate_count()[0] == 0) { + if (env->immediate_info()->count() == 0) { uv_check_stop(env->immediate_check_handle()); uv_idle_stop(env->immediate_idle_handle()); return true; @@ -307,12 +307,14 @@ void Environment::CheckImmediate(uv_check_t* handle) { env->RunAndClearNativeImmediates(); - MakeCallback(env->isolate(), - env->process_object(), - env->immediate_callback_function(), - 0, - nullptr, - {0, 0}).ToLocalChecked(); + do { + MakeCallback(env->isolate(), + env->process_object(), + env->immediate_callback_function(), + 0, + nullptr, + {0, 0}).ToLocalChecked(); + } while (env->immediate_info()->has_outstanding()); MaybeStopImmediate(env); } diff --git a/src/env.h b/src/env.h index d8012f7bcba762..b295347a9ae334 100644 --- a/src/env.h +++ b/src/env.h @@ -451,6 +451,30 @@ class Environment { DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope); }; + class ImmediateInfo { + public: + inline AliasedBuffer& fields(); + inline uint32_t count() const; + inline bool has_outstanding() const; + + inline void count_inc(uint32_t increment); + inline void count_dec(uint32_t decrement); + + private: + friend class Environment; // So we can call the constructor. + inline explicit ImmediateInfo(v8::Isolate* isolate); + + enum Fields { + kCount, + kHasOutstanding, + kFieldsCount + }; + + AliasedBuffer fields_; + + DISALLOW_COPY_AND_ASSIGN(ImmediateInfo); + }; + class TickInfo { public: inline AliasedBuffer& fields(); @@ -530,6 +554,7 @@ class Environment { inline void FinishHandleCleanup(uv_handle_t* handle); inline AsyncHooks* async_hooks(); + inline ImmediateInfo* immediate_info(); inline TickInfo* tick_info(); inline uint64_t timer_base() const; @@ -577,8 +602,6 @@ class Environment { inline double* fs_stats_field_array() const; inline void set_fs_stats_field_array(double* fields); - inline AliasedBuffer& scheduled_immediate_count(); - inline performance::performance_state* performance_state(); inline std::map* performance_marks(); @@ -686,6 +709,7 @@ class Environment { uv_check_t idle_check_handle_; AsyncHooks async_hooks_; + ImmediateInfo immediate_info_; TickInfo tick_info_; const uint64_t timer_base_; bool printed_error_; @@ -695,7 +719,6 @@ class Environment { size_t makecallback_cntr_; std::vector destroy_async_id_list_; - AliasedBuffer scheduled_immediate_count_; AliasedBuffer should_abort_on_uncaught_toggle_; int should_not_abort_scope_counter_ = 0; diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 5c3f499d163005..ab450fcb3e7346 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -90,8 +90,9 @@ class TimerWrap : public HandleWrap { env->NewFunctionTemplate(activate_cb)->GetFunction(env->context()) .ToLocalChecked(); auto result = Array::New(env->isolate(), 2); - result->Set(0, activate_function); - result->Set(1, env->scheduled_immediate_count().GetJSArray()); + result->Set(env->context(), 0, activate_function).FromJust(); + result->Set(env->context(), 1, + env->immediate_info()->fields().GetJSArray()).FromJust(); args.GetReturnValue().Set(result); } diff --git a/test/parallel/test-timers-immediate-queue-throw.js b/test/parallel/test-timers-immediate-queue-throw.js new file mode 100644 index 00000000000000..9929b27ab2fea3 --- /dev/null +++ b/test/parallel/test-timers-immediate-queue-throw.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); + +// setImmediate should run clear its queued cbs once per event loop turn +// but immediates queued while processing the current queue should happen +// on the next turn of the event loop. + +// In addition, if any setImmediate throws, the rest of the queue should +// be processed after all error handling is resolved, but that queue +// should not include any setImmediate calls scheduled after the +// processing of the queue started. + +let threw = false; +let stage = -1; + +const QUEUE = 10; + +const errObj = { + type: Error, + message: 'setImmediate Err' +}; + +process.once('uncaughtException', common.expectsError(errObj)); +process.once('uncaughtException', () => assert.strictEqual(stage, 0)); + +const d1 = domain.create(); +d1.once('error', common.expectsError(errObj)); +d1.once('error', () => assert.strictEqual(stage, 0)); + +const run = common.mustCall((callStage) => { + assert(callStage >= stage); + stage = callStage; + if (threw) + return; + + setImmediate(run, 2); +}, QUEUE * 3); + +for (let i = 0; i < QUEUE; i++) + setImmediate(run, 0); +setImmediate(() => { + threw = true; + process.nextTick(() => assert.strictEqual(stage, 1)); + throw new Error('setImmediate Err'); +}); +d1.run(() => setImmediate(() => { + throw new Error('setImmediate Err'); +})); +for (let i = 0; i < QUEUE; i++) + setImmediate(run, 1); From 2177138db0e10d9ce849e32c2910f85992d2b5ee Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sat, 13 Jan 2018 16:51:28 -0500 Subject: [PATCH 43/47] timers: allow Immediates to be unrefed Refactor Immediates handling to allow for them to be unrefed, similar to setTimeout, but without extra handles. Document the new `immediate.ref()` and `immediate.unref()` methods. Add SetImmediateUnref on the C++ side. Backport-PR-URL: https://github.com/nodejs/node/pull/19006 PR-URL: https://github.com/nodejs/node/pull/18139 Reviewed-By: Anna Henningsen Reviewed-By: Franziska Hinkelmann Reviewed-By: James M Snell --- doc/api/timers.md | 32 ++++ lib/timers.js | 143 ++++++++++-------- src/env-inl.h | 40 ++++- src/env.cc | 36 ++--- src/env.h | 17 ++- src/node_perf.cc | 18 +-- src/timer_wrap.cc | 12 +- test/addons-napi/test_uv_loop/test_uv_loop.cc | 9 ++ .../test-timers-immediate-unref-simple.js | 7 + test/parallel/test-timers-immediate-unref.js | 37 +++++ 10 files changed, 249 insertions(+), 102 deletions(-) create mode 100644 test/parallel/test-timers-immediate-unref-simple.js create mode 100644 test/parallel/test-timers-immediate-unref.js diff --git a/doc/api/timers.md b/doc/api/timers.md index 09502dee1003c8..13f2dea37d9d23 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -18,6 +18,38 @@ This object is created internally and is returned from [`setImmediate()`][]. It can be passed to [`clearImmediate()`][] in order to cancel the scheduled actions. +By default, when an immediate is scheduled, the Node.js event loop will continue +running as long as the immediate is active. The `Immediate` object returned by +[`setImmediate()`][] exports both `immediate.ref()` and `immediate.unref()` +functions that can be used to control this default behavior. + +### immediate.ref() + + +When called, requests that the Node.js event loop *not* exit so long as the +`Immediate` is active. Calling `immediate.ref()` multiple times will have no +effect. + +*Note*: By default, all `Immediate` objects are "ref'd", making it normally +unnecessary to call `immediate.ref()` unless `immediate.unref()` had been called +previously. + +Returns a reference to the `Immediate`. + +### immediate.unref() + + +When called, the active `Immediate` object will not require the Node.js event +loop to remain active. If there is no other activity keeping the event loop +running, the process may exit before the `Immediate` object's callback is +invoked. Calling `immediate.unref()` multiple times will have no effect. + +Returns a reference to the `Immediate`. + ## Class: Timeout This object is created internally and is returned from [`setTimeout()`][] and diff --git a/lib/timers.js b/lib/timers.js index 5a9073756fc1fe..46cd770fc643bd 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -52,11 +52,14 @@ const trigger_async_id_symbol = Symbol('triggerAsyncId'); // *Must* match Environment::ImmediateInfo::Fields in src/env.h. const kCount = 0; -const kHasOutstanding = 1; +const kRefCount = 1; +const kHasOutstanding = 2; -const [activateImmediateCheck, immediateInfo] = +const [immediateInfo, toggleImmediateRef] = setImmediateCallback(processImmediate); +const kRefed = Symbol('refed'); + // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2 ** 31 - 1; @@ -690,42 +693,41 @@ function processImmediate() { const queue = outstandingQueue.head !== null ? outstandingQueue : immediateQueue; var immediate = queue.head; - var tail = queue.tail; + const tail = queue.tail; // Clear the linked list early in case new `setImmediate()` calls occur while // immediate callbacks are executed queue.head = queue.tail = null; - while (immediate !== null) { - if (!immediate._onImmediate) { - immediate = immediate._idleNext; - continue; - } + let count = 0; + let refCount = 0; - // Save next in case `clearImmediate(immediate)` is called from callback - const next = immediate._idleNext; + while (immediate !== null) { + immediate._destroyed = true; const asyncId = immediate[async_id_symbol]; emitBefore(asyncId, immediate[trigger_async_id_symbol]); - tryOnImmediate(immediate, next, tail); + count++; + if (immediate[kRefed]) + refCount++; + immediate[kRefed] = undefined; + + tryOnImmediate(immediate, tail, count, refCount); emitAfter(asyncId); - // If `clearImmediate(immediate)` wasn't called from the callback, use the - // `immediate`'s next item - if (immediate._idleNext !== null) - immediate = immediate._idleNext; - else - immediate = next; + immediate = immediate._idleNext; } + immediateInfo[kCount] -= count; + immediateInfo[kRefCount] -= refCount; immediateInfo[kHasOutstanding] = 0; } // An optimization so that the try/finally only de-optimizes (since at least v8 // 4.7) what is in this smaller function. -function tryOnImmediate(immediate, next, oldTail) { +function tryOnImmediate(immediate, oldTail, count, refCount) { var threw = true; try { // make the actual call outside the try/finally to allow it to be optimized @@ -734,21 +736,21 @@ function tryOnImmediate(immediate, next, oldTail) { } finally { immediate._onImmediate = null; - if (!immediate._destroyed) { - immediate._destroyed = true; - immediateInfo[kCount]--; - - if (async_hook_fields[kDestroy] > 0) { - emitDestroy(immediate[async_id_symbol]); - } + if (async_hook_fields[kDestroy] > 0) { + emitDestroy(immediate[async_id_symbol]); } - if (threw && (immediate._idleNext !== null || next !== null)) { - // Handle any remaining Immediates after error handling has resolved, - // assuming we're still alive to do so. - outstandingQueue.head = immediate._idleNext || next; - outstandingQueue.tail = oldTail; - immediateInfo[kHasOutstanding] = 1; + if (threw) { + immediateInfo[kCount] -= count; + immediateInfo[kRefCount] -= refCount; + + if (immediate._idleNext !== null) { + // Handle any remaining Immediates after error handling has resolved, + // assuming we're still alive to do so. + outstandingQueue.head = immediate._idleNext; + outstandingQueue.tail = oldTail; + immediateInfo[kHasOutstanding] = 1; + } } } } @@ -763,31 +765,51 @@ function runCallback(timer) { } -function Immediate(callback, args) { - this._idleNext = null; - this._idlePrev = null; - // this must be set to null first to avoid function tracking - // on the hidden class, revisit in V8 versions after 6.2 - this._onImmediate = null; - this._onImmediate = callback; - this._argv = args; - this._destroyed = false; +const Immediate = class Immediate { + constructor(callback, args) { + this._idleNext = null; + this._idlePrev = null; + // this must be set to null first to avoid function tracking + // on the hidden class, revisit in V8 versions after 6.2 + this._onImmediate = null; + this._onImmediate = callback; + this._argv = args; + this._destroyed = false; + this[kRefed] = false; + + this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; + this[trigger_async_id_symbol] = getDefaultTriggerAsyncId(); + if (async_hook_fields[kInit] > 0) { + emitInit(this[async_id_symbol], + 'Immediate', + this[trigger_async_id_symbol], + this); + } - this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; - this[trigger_async_id_symbol] = getDefaultTriggerAsyncId(); - if (async_hook_fields[kInit] > 0) { - emitInit(this[async_id_symbol], - 'Immediate', - this[trigger_async_id_symbol], - this); + this.ref(); + immediateInfo[kCount]++; + + immediateQueue.append(this); } - if (immediateInfo[kCount] === 0) - activateImmediateCheck(); - immediateInfo[kCount]++; + ref() { + if (this[kRefed] === false) { + this[kRefed] = true; + if (immediateInfo[kRefCount]++ === 0) + toggleImmediateRef(true); + } + return this; + } - immediateQueue.append(this); -} + unref() { + if (this[kRefed] === true) { + this[kRefed] = false; + if (--immediateInfo[kRefCount] === 0) + toggleImmediateRef(false); + } + return this; + } +}; function setImmediate(callback, arg1, arg2, arg3) { if (typeof callback !== 'function') { @@ -827,15 +849,18 @@ exports.setImmediate = setImmediate; exports.clearImmediate = function(immediate) { - if (!immediate) return; + if (!immediate || immediate._destroyed) + return; - if (!immediate._destroyed) { - immediateInfo[kCount]--; - immediate._destroyed = true; + immediateInfo[kCount]--; + immediate._destroyed = true; - if (async_hook_fields[kDestroy] > 0) { - emitDestroy(immediate[async_id_symbol]); - } + if (immediate[kRefed] && --immediateInfo[kRefCount] === 0) + toggleImmediateRef(false); + immediate[kRefed] = undefined; + + if (async_hook_fields[kDestroy] > 0) { + emitDestroy(immediate[async_id_symbol]); } immediate._onImmediate = null; diff --git a/src/env-inl.h b/src/env-inl.h index 0328f058de3ab0..37d1cf172ea14b 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -229,6 +229,10 @@ inline uint32_t Environment::ImmediateInfo::count() const { return fields_[kCount]; } +inline uint32_t Environment::ImmediateInfo::ref_count() const { + return fields_[kRefCount]; +} + inline bool Environment::ImmediateInfo::has_outstanding() const { return fields_[kHasOutstanding] == 1; } @@ -241,6 +245,14 @@ inline void Environment::ImmediateInfo::count_dec(uint32_t decrement) { fields_[kCount] = fields_[kCount] - decrement; } +inline void Environment::ImmediateInfo::ref_count_inc(uint32_t increment) { + fields_[kRefCount] = fields_[kRefCount] + increment; +} + +inline void Environment::ImmediateInfo::ref_count_dec(uint32_t decrement) { + fields_[kRefCount] = fields_[kRefCount] - decrement; +} + inline Environment::TickInfo::TickInfo(v8::Isolate* isolate) : fields_(isolate, kFieldsCount) {} @@ -514,20 +526,36 @@ inline void Environment::set_fs_stats_field_array(double* fields) { fs_stats_field_array_ = fields; } -void Environment::SetImmediate(native_immediate_callback cb, +void Environment::CreateImmediate(native_immediate_callback cb, void* data, - v8::Local obj) { + v8::Local obj, + bool ref) { native_immediate_callbacks_.push_back({ cb, data, - std::unique_ptr>( - obj.IsEmpty() ? nullptr : new v8::Persistent(isolate_, obj)) + std::unique_ptr>(obj.IsEmpty() ? + nullptr : new v8::Persistent(isolate_, obj)), + ref }); - if (immediate_info()->count() == 0) - ActivateImmediateCheck(); immediate_info()->count_inc(1); } +void Environment::SetImmediate(native_immediate_callback cb, + void* data, + v8::Local obj) { + CreateImmediate(cb, data, obj, true); + + if (immediate_info()->ref_count() == 0) + ToggleImmediateRef(true); + immediate_info()->ref_count_inc(1); +} + +void Environment::SetUnrefImmediate(native_immediate_callback cb, + void* data, + v8::Local obj) { + CreateImmediate(cb, data, obj, false); +} + inline performance::performance_state* Environment::performance_state() { return performance_state_.get(); } diff --git a/src/env.cc b/src/env.cc index e4afb35786e2af..17cdbbb79f9af1 100644 --- a/src/env.cc +++ b/src/env.cc @@ -80,6 +80,8 @@ void Environment::Start(int argc, uv_idle_init(event_loop(), immediate_idle_handle()); + uv_check_start(immediate_check_handle(), CheckImmediate); + // Inform V8's CPU profiler when we're idle. The profiler is sampling-based // but not all samples are created equal; mark the wall clock time spent in // epoll_wait() and friends so profiling tools can filter it out. The samples @@ -272,39 +274,35 @@ void Environment::EnvPromiseHook(v8::PromiseHookType type, void Environment::RunAndClearNativeImmediates() { size_t count = native_immediate_callbacks_.size(); if (count > 0) { + size_t ref_count = 0; std::vector list; native_immediate_callbacks_.swap(list); for (const auto& cb : list) { cb.cb_(this, cb.data_); if (cb.keep_alive_) cb.keep_alive_->Reset(); + if (cb.refed_) + ref_count++; } #ifdef DEBUG CHECK_GE(immediate_info()->count(), count); #endif immediate_info()->count_dec(count); + immediate_info()->ref_count_dec(ref_count); } } -static bool MaybeStopImmediate(Environment* env) { - if (env->immediate_info()->count() == 0) { - uv_check_stop(env->immediate_check_handle()); - uv_idle_stop(env->immediate_idle_handle()); - return true; - } - return false; -} - void Environment::CheckImmediate(uv_check_t* handle) { Environment* env = Environment::from_immediate_check_handle(handle); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - if (MaybeStopImmediate(env)) + if (env->immediate_info()->count() == 0) return; + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + env->RunAndClearNativeImmediates(); do { @@ -316,13 +314,17 @@ void Environment::CheckImmediate(uv_check_t* handle) { {0, 0}).ToLocalChecked(); } while (env->immediate_info()->has_outstanding()); - MaybeStopImmediate(env); + if (env->immediate_info()->ref_count() == 0) + env->ToggleImmediateRef(false); } -void Environment::ActivateImmediateCheck() { - uv_check_start(&immediate_check_handle_, CheckImmediate); - // Idle handle is needed only to stop the event loop from blocking in poll. - uv_idle_start(&immediate_idle_handle_, [](uv_idle_t*){ }); +void Environment::ToggleImmediateRef(bool ref) { + if (ref) { + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_start(immediate_idle_handle(), [](uv_idle_t*){ }); + } else { + uv_idle_stop(immediate_idle_handle()); + } } void Environment::AsyncHooks::grow_async_ids_stack() { diff --git a/src/env.h b/src/env.h index b295347a9ae334..b834585edd79c5 100644 --- a/src/env.h +++ b/src/env.h @@ -455,17 +455,22 @@ class Environment { public: inline AliasedBuffer& fields(); inline uint32_t count() const; + inline uint32_t ref_count() const; inline bool has_outstanding() const; inline void count_inc(uint32_t increment); inline void count_dec(uint32_t decrement); + inline void ref_count_inc(uint32_t increment); + inline void ref_count_dec(uint32_t decrement); + private: friend class Environment; // So we can call the constructor. inline explicit ImmediateInfo(v8::Isolate* isolate); enum Fields { kCount, + kRefCount, kHasOutstanding, kFieldsCount }; @@ -680,8 +685,12 @@ class Environment { inline void SetImmediate(native_immediate_callback cb, void* data, v8::Local obj = v8::Local()); + inline void SetUnrefImmediate(native_immediate_callback cb, + void* data, + v8::Local obj = + v8::Local()); // This needs to be available for the JS-land setImmediate(). - void ActivateImmediateCheck(); + void ToggleImmediateRef(bool ref); class ShouldNotAbortOnUncaughtScope { public: @@ -698,6 +707,11 @@ class Environment { static inline Environment* ForAsyncHooks(AsyncHooks* hooks); private: + inline void CreateImmediate(native_immediate_callback cb, + void* data, + v8::Local obj, + bool ref); + inline void ThrowError(v8::Local (*fun)(v8::Local), const char* errmsg); @@ -761,6 +775,7 @@ class Environment { native_immediate_callback cb_; void* data_; std::unique_ptr> keep_alive_; + bool refed_; }; std::vector native_immediate_callbacks_; void RunAndClearNativeImmediates(); diff --git a/src/node_perf.cc b/src/node_perf.cc index 8862e5fab755a1..8ee805a8382c4e 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -184,9 +184,8 @@ void SetupPerformanceObservers(const FunctionCallbackInfo& args) { } // Creates a GC Performance Entry and passes it to observers -void PerformanceGCCallback(uv_async_t* handle) { - GCPerformanceEntry* entry = static_cast(handle->data); - Environment* env = entry->env(); +void PerformanceGCCallback(Environment* env, void* ptr) { + GCPerformanceEntry* entry = static_cast(ptr); HandleScope scope(env->isolate()); Local context = env->context(); @@ -204,10 +203,6 @@ void PerformanceGCCallback(uv_async_t* handle) { } delete entry; - auto closeCB = [](uv_handle_t* handle) { - delete reinterpret_cast(handle); - }; - uv_close(reinterpret_cast(handle), closeCB); } // Marks the start of a GC cycle @@ -224,16 +219,13 @@ void MarkGarbageCollectionEnd(Isolate* isolate, v8::GCCallbackFlags flags, void* data) { Environment* env = static_cast(data); - uv_async_t* async = new uv_async_t(); - if (uv_async_init(env->event_loop(), async, PerformanceGCCallback)) - return delete async; - uv_unref(reinterpret_cast(async)); - async->data = + GCPerformanceEntry* entry = new GCPerformanceEntry(env, static_cast(type), performance_last_gc_start_mark_, PERFORMANCE_NOW()); - CHECK_EQ(0, uv_async_send(async)); + env->SetUnrefImmediate(PerformanceGCCallback, + entry); } diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index ab450fcb3e7346..1725cf30e71d04 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -83,16 +83,16 @@ class TimerWrap : public HandleWrap { CHECK(args[0]->IsFunction()); auto env = Environment::GetCurrent(args); env->set_immediate_callback_function(args[0].As()); - auto activate_cb = [] (const FunctionCallbackInfo& args) { - Environment::GetCurrent(args)->ActivateImmediateCheck(); + auto toggle_ref_cb = [] (const FunctionCallbackInfo& args) { + Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue()); }; - auto activate_function = - env->NewFunctionTemplate(activate_cb)->GetFunction(env->context()) + auto toggle_ref_function = + env->NewFunctionTemplate(toggle_ref_cb)->GetFunction(env->context()) .ToLocalChecked(); auto result = Array::New(env->isolate(), 2); - result->Set(env->context(), 0, activate_function).FromJust(); - result->Set(env->context(), 1, + result->Set(env->context(), 0, env->immediate_info()->fields().GetJSArray()).FromJust(); + result->Set(env->context(), 1, toggle_ref_function).FromJust(); args.GetReturnValue().Set(result); } diff --git a/test/addons-napi/test_uv_loop/test_uv_loop.cc b/test/addons-napi/test_uv_loop/test_uv_loop.cc index 44819f72bb6b9d..048e25af9ddfb3 100644 --- a/test/addons-napi/test_uv_loop/test_uv_loop.cc +++ b/test/addons-napi/test_uv_loop/test_uv_loop.cc @@ -24,6 +24,15 @@ void* SetImmediate(napi_env env, T&& cb) { assert(cb() != nullptr); }); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_t* idle = new uv_idle_t; + uv_idle_init(loop, idle); + uv_idle_start(idle, [](uv_idle_t* idle) { + uv_close(reinterpret_cast(idle), [](uv_handle_t* handle) { + delete reinterpret_cast(handle); + }); + }); + return nullptr; } diff --git a/test/parallel/test-timers-immediate-unref-simple.js b/test/parallel/test-timers-immediate-unref-simple.js new file mode 100644 index 00000000000000..68497460328c32 --- /dev/null +++ b/test/parallel/test-timers-immediate-unref-simple.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); + +// This immediate should not execute as it was unrefed +// and nothing else is keeping the event loop alive +setImmediate(common.mustNotCall()).unref(); diff --git a/test/parallel/test-timers-immediate-unref.js b/test/parallel/test-timers-immediate-unref.js new file mode 100644 index 00000000000000..5b56eb7e1d8e5b --- /dev/null +++ b/test/parallel/test-timers-immediate-unref.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); + +// This immediate should execute as it was unrefed and refed again. +// It also confirms that unref/ref are chainable. +setImmediate(common.mustCall(firstStep)).ref().unref().unref().ref(); + +function firstStep() { + const countdown = + new Countdown(2, common.mustCall(() => setImmediate(secondStep))); + // Unrefed setImmediate executes if it was unrefed but something else keeps + // the loop open + setImmediate(() => countdown.dec()).unref(); + setTimeout(() => countdown.dec(), 50); +} + +function secondStep() { + // clearImmediate works just fine with unref'd immediates + const immA = setImmediate(() => { + clearImmediate(immA); + clearImmediate(immB); + // this should not keep the event loop open indefinitely + // or do anything else weird + immA.ref(); + immB.ref(); + }).unref(); + const immB = setImmediate(common.mustNotCall()).unref(); + setImmediate(common.mustCall(finalStep)); +} + +function finalStep() { + // This immediate should not execute as it was unrefed + // and nothing else is keeping the event loop alive + setImmediate(common.mustNotCall()).unref(); +} From b04d42d1e3b63bdb24d8cacb7034ec6528e430aa Mon Sep 17 00:00:00 2001 From: Matheus Marchini Date: Mon, 25 Dec 2017 22:17:25 -0200 Subject: [PATCH 44/47] src, test: node internals' postmortem metadata Before these changes, only V8 added postmortem metadata to Node's binary, limiting the possibilities for debugger's developers to add some features that rely on investigating Node's internal structures. These changes are first steps towards empowering debug tools to navigate Node's internal structures. One example of what can be achieved with this is shown at nodejs/llnode#122 (a command which prints information about handles and requests on the queue for a core dump file). Node postmortem metadata are prefixed with nodedbg_. This also adds tests to validate if all postmortem metadata are calculated correctly, plus some documentation on what is postmortem metadata and a few care to be taken to avoid breaking it. Ref: https://github.com/nodejs/llnode/pull/122 Ref: https://github.com/nodejs/post-mortem/issues/46 PR-URL: https://github.com/nodejs/node/pull/14901 Refs: https://github.com/nodejs/post-mortem/issues/46 Reviewed-By: Michael Dawson Reviewed-By: Matteo Collina Reviewed-By: Ben Noordhuis Reviewed-By: Joyee Cheung --- doc/guides/node-postmortem-support.md | 72 +++++++++ node.gyp | 3 + src/base_object.h | 5 + src/env.h | 6 + src/handle_wrap.h | 5 + src/node_postmortem_metadata.cc | 118 +++++++++++++++ src/req_wrap.h | 10 +- src/util-inl.h | 12 +- test/cctest/node_test_fixture.h | 55 +++++++ test/cctest/test_environment.cc | 46 +----- test/cctest/test_node_postmortem_metadata.cc | 148 +++++++++++++++++++ 11 files changed, 432 insertions(+), 48 deletions(-) create mode 100644 doc/guides/node-postmortem-support.md create mode 100644 src/node_postmortem_metadata.cc create mode 100644 test/cctest/test_node_postmortem_metadata.cc diff --git a/doc/guides/node-postmortem-support.md b/doc/guides/node-postmortem-support.md new file mode 100644 index 00000000000000..e29d9ca3a1fdf0 --- /dev/null +++ b/doc/guides/node-postmortem-support.md @@ -0,0 +1,72 @@ +# Postmortem Support + +Postmortem metadata are constants present in the final build which can be used +by debuggers and other tools to navigate through internal structures of software +when analyzing its memory (either on a running process or a core dump). Node +provides this metadata in its builds for V8 and Node internal structures. + + +### V8 Postmortem metadata + +V8 prefixes all postmortem constants with `v8dbg_`, and they allow inspection of +objects on the heap as well as object properties and references. V8 generates +those symbols with a script (`deps/v8/tools/gen-postmortem-metadata.py`), and +Node always includes these constants in the final build. + +### Node Debug Symbols + +Node prefixes all postmortem constants with `nodedbg_`, and they complement V8 +constants by providing ways to inspect Node-specific structures, like +`node::Environment`, `node::BaseObject` and its descendants, classes from +`src/utils.h` and others. Those constants are declared in +`src/node_postmortem_metadata.cc`, and most of them are calculated at compile +time. + +#### Calculating offset of class members + +Node constants referring to the offset of class members in memory are calculated +at compile time. Because of that, those class members must be at a fixed offset +from the start of the class. That's not a problem in most cases, but it also +means that those members should always come after any templated member on the +class definition. + +For example, if we want to add a constant with the offset for +`ReqWrap::req_wrap_queue_`, it should be defined after `ReqWrap::req_`, because +`sizeof(req_)` depends on the type of T, which means the class definition should +be like this: + +```c++ +template +class ReqWrap : public AsyncWrap { + private: + // req_wrap_queue_ comes before any templated member, which places it in a + // fixed offset from the start of the class + ListNode req_wrap_queue_; + + T req_; +}; +``` + +instead of: + +```c++ +template +class ReqWrap : public AsyncWrap { + private: + T req_; + + // req_wrap_queue_ comes after a templated member, which means it won't be in + // a fixed offset from the start of the class + ListNode req_wrap_queue_; +}; +``` + +There are also tests on `test/cctest/test_node_postmortem_metadata.cc` to make +sure all Node postmortem metadata are calculated correctly. + +## Tools and References + +* [llnode](https://github.com/nodejs/llnode): LLDB plugin +* [`mdb_v8`](https://github.com/joyent/mdb_v8): mdb plugin +* [nodejs/post-mortem](https://github.com/nodejs/post-mortem): Node.js +post-mortem working group diff --git a/node.gyp b/node.gyp index 3320e3b4cd122c..673b8fdb7ebd09 100644 --- a/node.gyp +++ b/node.gyp @@ -308,6 +308,7 @@ 'src/node_os.cc', 'src/node_platform.cc', 'src/node_perf.cc', + 'src/node_postmortem_metadata.cc', 'src/node_serdes.cc', 'src/node_trace_events.cc', 'src/node_url.cc', @@ -968,6 +969,7 @@ 'test/cctest/node_test_fixture.cc', 'test/cctest/test_aliased_buffer.cc', 'test/cctest/test_base64.cc', + 'test/cctest/test_node_postmortem_metadata.cc', 'test/cctest/test_environment.cc', 'test/cctest/test_util.cc', 'test/cctest/test_url.cc' @@ -975,6 +977,7 @@ 'libraries': [ '<(obj_path)<(obj_separator)async_wrap.<(obj_suffix)', + '<(obj_path)<(obj_separator)handle_wrap.<(obj_suffix)', '<(obj_path)<(obj_separator)env.<(obj_suffix)', '<(obj_path)<(obj_separator)node.<(obj_suffix)', '<(obj_path)<(obj_separator)node_buffer.<(obj_suffix)', diff --git a/src/base_object.h b/src/base_object.h index 0998920f49dd15..5852f764066fbc 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -65,6 +65,11 @@ class BaseObject { static inline void WeakCallback( const v8::WeakCallbackInfo& data); + // persistent_handle_ needs to be at a fixed offset from the start of the + // class because it is used by src/node_postmortem_metadata.cc to calculate + // offsets and generate debug symbols for BaseObject, which assumes that the + // position of members in memory are predictable. For more information please + // refer to `doc/guides/node-postmortem-support.md` v8::Persistent persistent_handle_; Environment* env_; }; diff --git a/src/env.h b/src/env.h index b834585edd79c5..b0978db33072df 100644 --- a/src/env.h +++ b/src/env.h @@ -744,6 +744,12 @@ class Environment { std::unique_ptr inspector_agent_; #endif + // handle_wrap_queue_ and req_wrap_queue_ needs to be at a fixed offset from + // the start of the class because it is used by + // src/node_postmortem_metadata.cc to calculate offsets and generate debug + // symbols for Environment, which assumes that the position of members in + // memory are predictable. For more information please refer to + // `doc/guides/node-postmortem-support.md` HandleWrapQueue handle_wrap_queue_; ReqWrapQueue req_wrap_queue_; ListHead&); static void OnClose(uv_handle_t* handle); + // handle_wrap_queue_ needs to be at a fixed offset from the start of the + // class because it is used by src/node_postmortem_metadata.cc to calculate + // offsets and generate debug symbols for HandleWrap, which assumes that the + // position of members in memory are predictable. For more information please + // refer to `doc/guides/node-postmortem-support.md` ListNode handle_wrap_queue_; enum { kInitialized, kClosing, kClosingWithCallback, kClosed } state_; uv_handle_t* const handle_; diff --git a/src/node_postmortem_metadata.cc b/src/node_postmortem_metadata.cc new file mode 100644 index 00000000000000..4a463958f54509 --- /dev/null +++ b/src/node_postmortem_metadata.cc @@ -0,0 +1,118 @@ +// Need to import standard headers before redefining private, otherwise it +// won't compile. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace node { +// Forward declaration needed before redefining private. +int GenDebugSymbols(); +} // namespace node + + +#define private friend int GenDebugSymbols(); private + +#include "env.h" +#include "base_object-inl.h" +#include "handle_wrap.h" +#include "util-inl.h" +#include "req_wrap.h" +#include "v8abbr.h" + +#define NODEDBG_SYMBOL(Name) nodedbg_ ## Name + +// nodedbg_offset_CLASS__MEMBER__TYPE: Describes the offset to a class member. +#define NODEDBG_OFFSET(Class, Member, Type) \ + NODEDBG_SYMBOL(offset_ ## Class ## __ ## Member ## __ ## Type) + +// These are the constants describing Node internal structures. Every constant +// should use the format described above. These constants are declared as +// global integers so that they'll be present in the generated node binary. They +// also need to be declared outside any namespace to avoid C++ name-mangling. +#define NODE_OFFSET_POSTMORTEM_METADATA(V) \ + V(BaseObject, persistent_handle_, v8_Persistent_v8_Object, \ + BaseObject::persistent_handle_) \ + V(Environment, handle_wrap_queue_, Environment_HandleWrapQueue, \ + Environment::handle_wrap_queue_) \ + V(Environment, req_wrap_queue_, Environment_ReqWrapQueue, \ + Environment::req_wrap_queue_) \ + V(HandleWrap, handle_wrap_queue_, ListNode_HandleWrap, \ + HandleWrap::handle_wrap_queue_) \ + V(Environment_HandleWrapQueue, head_, ListNode_HandleWrap, \ + Environment::HandleWrapQueue::head_) \ + V(ListNode_HandleWrap, next_, uintptr_t, ListNode::next_) \ + V(ReqWrap, req_wrap_queue_, ListNode_ReqWrapQueue, \ + ReqWrap::req_wrap_queue_) \ + V(Environment_ReqWrapQueue, head_, ListNode_ReqWrapQueue, \ + Environment::ReqWrapQueue::head_) \ + V(ListNode_ReqWrap, next_, uintptr_t, ListNode>::next_) + +extern "C" { +int nodedbg_const_Environment__kContextEmbedderDataIndex__int; +uintptr_t nodedbg_offset_ExternalString__data__uintptr_t; + +#define V(Class, Member, Type, Accessor) \ + NODE_EXTERN uintptr_t NODEDBG_OFFSET(Class, Member, Type); + NODE_OFFSET_POSTMORTEM_METADATA(V) +#undef V +} + +namespace node { + +int GenDebugSymbols() { + nodedbg_const_Environment__kContextEmbedderDataIndex__int = + Environment::kContextEmbedderDataIndex; + + nodedbg_offset_ExternalString__data__uintptr_t = NODE_OFF_EXTSTR_DATA; + + #define V(Class, Member, Type, Accessor) \ + NODEDBG_OFFSET(Class, Member, Type) = OffsetOf(&Accessor); + NODE_OFFSET_POSTMORTEM_METADATA(V) + #undef V + + return 1; +} + +int debug_symbols_generated = GenDebugSymbols(); + +} // namespace node diff --git a/src/req_wrap.h b/src/req_wrap.h index 83baf9d2a35285..05bc558570abf1 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -27,9 +27,13 @@ class ReqWrap : public AsyncWrap { protected: // req_wrap_queue_ needs to be at a fixed offset from the start of the class // because it is used by ContainerOf to calculate the address of the embedding - // ReqWrap. ContainerOf compiles down to simple, fixed pointer arithmetic. - // sizeof(req_) depends on the type of T, so req_wrap_queue_ would - // no longer be at a fixed offset if it came after req_. + // ReqWrap. ContainerOf compiles down to simple, fixed pointer arithmetic. It + // is also used by src/node_postmortem_metadata.cc to calculate offsets and + // generate debug symbols for ReqWrap, which assumes that the position of + // members in memory are predictable. sizeof(req_) depends on the type of T, + // so req_wrap_queue_ would no longer be at a fixed offset if it came after + // req_. For more information please refer to + // `doc/guides/node-postmortem-support.md` T req_; }; diff --git a/src/util-inl.h b/src/util-inl.h index 558a0ab2b42611..c5a25c91ffb088 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -141,13 +141,17 @@ typename ListHead::Iterator ListHead::end() const { return Iterator(const_cast*>(&head_)); } +template +constexpr uintptr_t OffsetOf(Inner Outer::*field) { + return reinterpret_cast(&(static_cast(0)->*field)); +} + template ContainerOfHelper::ContainerOfHelper(Inner Outer::*field, Inner* pointer) - : pointer_(reinterpret_cast( - reinterpret_cast(pointer) - - reinterpret_cast(&(static_cast(0)->*field)))) { -} + : pointer_( + reinterpret_cast( + reinterpret_cast(pointer) - OffsetOf(field))) {} template template diff --git a/test/cctest/node_test_fixture.h b/test/cctest/node_test_fixture.h index 5080f0334808eb..583530c65ebe9e 100644 --- a/test/cctest/node_test_fixture.h +++ b/test/cctest/node_test_fixture.h @@ -5,6 +5,7 @@ #include "gtest/gtest.h" #include "node.h" #include "node_platform.h" +#include "node_internals.h" #include "env.h" #include "v8.h" #include "libplatform/libplatform.h" @@ -73,6 +74,13 @@ class NodeTestFixture : public ::testing::Test { CHECK_EQ(0, uv_loop_init(¤t_loop)); v8::V8::InitializePlatform(platform.get()); v8::V8::Initialize(); + + // As the TracingController is stored globally, we only need to create it + // one time for all tests. + if (node::tracing::TraceEventHelper::GetTracingController() == nullptr) { + node::tracing::TraceEventHelper::SetTracingController( + new v8::TracingController()); + } } static void TearDownTestCase() { @@ -95,4 +103,51 @@ class NodeTestFixture : public ::testing::Test { } }; + +class EnvironmentTestFixture : public NodeTestFixture { + public: + class Env { + public: + Env(const v8::HandleScope& handle_scope, + const Argv& argv, + NodeTestFixture* test_fixture) { + auto isolate = handle_scope.GetIsolate(); + context_ = node::NewContext(isolate); + CHECK(!context_.IsEmpty()); + context_->Enter(); + + isolate_data_ = node::CreateIsolateData(isolate, + NodeTestFixture::CurrentLoop(), + test_fixture->Platform()); + CHECK_NE(nullptr, isolate_data_); + environment_ = node::CreateEnvironment(isolate_data_, + context_, + 1, *argv, + argv.nr_args(), *argv); + CHECK_NE(nullptr, environment_); + } + + ~Env() { + environment_->CleanupHandles(); + node::FreeEnvironment(environment_); + node::FreeIsolateData(isolate_data_); + context_->Exit(); + } + + node::Environment* operator*() const { + return environment_; + } + + v8::Local context() const { + return context_; + } + + private: + v8::Local context_; + node::IsolateData* isolate_data_; + node::Environment* environment_; + DISALLOW_COPY_AND_ASSIGN(Env); + }; +}; + #endif // TEST_CCTEST_NODE_TEST_FIXTURE_H_ diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 704efd7a88358f..352fed1fb62ed9 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -20,43 +20,7 @@ static void at_exit_callback1(void* arg); static void at_exit_callback2(void* arg); static std::string cb_1_arg; // NOLINT(runtime/string) -class EnvironmentTest : public NodeTestFixture { - public: - class Env { - public: - Env(const v8::HandleScope& handle_scope, - v8::Isolate* isolate, - const Argv& argv, - NodeTestFixture* test_fixture) { - context_ = v8::Context::New(isolate); - CHECK(!context_.IsEmpty()); - isolate_data_ = CreateIsolateData(isolate, - NodeTestFixture::CurrentLoop(), - test_fixture->Platform()); - CHECK_NE(nullptr, isolate_data_); - environment_ = CreateEnvironment(isolate_data_, - context_, - 1, *argv, - argv.nr_args(), *argv); - CHECK_NE(nullptr, environment_); - } - - ~Env() { - environment_->CleanupHandles(); - FreeEnvironment(environment_); - FreeIsolateData(isolate_data_); - } - - Environment* operator*() const { - return environment_; - } - - private: - v8::Local context_; - IsolateData* isolate_data_; - Environment* environment_; - }; - +class EnvironmentTest : public EnvironmentTestFixture { private: virtual void TearDown() { NodeTestFixture::TearDown(); @@ -68,7 +32,7 @@ class EnvironmentTest : public NodeTestFixture { TEST_F(EnvironmentTest, AtExitWithEnvironment) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env {handle_scope, isolate_, argv, this}; + Env env {handle_scope, argv, this}; AtExit(*env, at_exit_callback1); RunAtExit(*env); @@ -78,7 +42,7 @@ TEST_F(EnvironmentTest, AtExitWithEnvironment) { TEST_F(EnvironmentTest, AtExitWithArgument) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env {handle_scope, isolate_, argv, this}; + Env env {handle_scope, argv, this}; std::string arg{"some args"}; AtExit(*env, at_exit_callback1, static_cast(&arg)); @@ -89,8 +53,8 @@ TEST_F(EnvironmentTest, AtExitWithArgument) { TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env1 {handle_scope, isolate_, argv, this}; - Env env2 {handle_scope, isolate_, argv, this}; + Env env1 {handle_scope, argv, this}; + Env env2 {handle_scope, argv, this}; AtExit(*env1, at_exit_callback1); AtExit(*env2, at_exit_callback2); diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc new file mode 100644 index 00000000000000..9ba6e15593a019 --- /dev/null +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -0,0 +1,148 @@ +#include "node_postmortem_metadata.cc" + +#include "gtest/gtest.h" +#include "node.h" +#include "node_internals.h" +#include "node_test_fixture.h" +#include "req_wrap-inl.h" +#include "tracing/agent.h" +#include "v8.h" + + +class DebugSymbolsTest : public EnvironmentTestFixture {}; + + +class TestHandleWrap : public node::HandleWrap { + public: + size_t self_size() const override { return sizeof(*this); } + + TestHandleWrap(node::Environment* env, + v8::Local object, + uv_tcp_t* handle) + : node::HandleWrap(env, + object, + reinterpret_cast(handle), + node::AsyncWrap::PROVIDER_TIMERWRAP) {} +}; + + +class TestReqWrap : public node::ReqWrap { + public: + size_t self_size() const override { return sizeof(*this); } + + TestReqWrap(node::Environment* env, v8::Local object) + : node::ReqWrap(env, + object, + node::AsyncWrap::PROVIDER_TIMERWRAP) {} +}; + +TEST_F(DebugSymbolsTest, ContextEmbedderDataIndex) { + int kContextEmbedderDataIndex = node::Environment::kContextEmbedderDataIndex; + EXPECT_EQ(nodedbg_const_Environment__kContextEmbedderDataIndex__int, + kContextEmbedderDataIndex); +} + +TEST_F(DebugSymbolsTest, ExternalStringDataOffset) { + EXPECT_EQ(nodedbg_offset_ExternalString__data__uintptr_t, + NODE_OFF_EXTSTR_DATA); +} + +TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env{handle_scope, argv, this}; + + v8::Local object = v8::Object::New(isolate_); + node::BaseObject obj(*env, object); + + auto expected = reinterpret_cast(&obj.persistent()); + auto calculated = reinterpret_cast(&obj) + + nodedbg_offset_BaseObject__persistent_handle___v8_Persistent_v8_Object; + EXPECT_EQ(expected, calculated); + + obj.persistent().Reset(); // ~BaseObject() expects an empty handle. +} + + +TEST_F(DebugSymbolsTest, EnvironmentHandleWrapQueue) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env{handle_scope, argv, this}; + + auto expected = reinterpret_cast((*env)->handle_wrap_queue()); + auto calculated = reinterpret_cast(*env) + + nodedbg_offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue; // NOLINT(whitespace/line_length) + EXPECT_EQ(expected, calculated); +} + +TEST_F(DebugSymbolsTest, EnvironmentReqWrapQueue) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env{handle_scope, argv, this}; + + auto expected = reinterpret_cast((*env)->req_wrap_queue()); + auto calculated = reinterpret_cast(*env) + + nodedbg_offset_Environment__req_wrap_queue___Environment_ReqWrapQueue; + EXPECT_EQ(expected, calculated); +} + +TEST_F(DebugSymbolsTest, HandleWrapList) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env{handle_scope, argv, this}; + + uv_tcp_t handle; + + auto obj_template = v8::FunctionTemplate::New(isolate_); + obj_template->InstanceTemplate()->SetInternalFieldCount(1); + + v8::Local object = + obj_template->GetFunction()->NewInstance(env.context()).ToLocalChecked(); + TestHandleWrap obj(*env, object, &handle); + + auto queue = reinterpret_cast((*env)->handle_wrap_queue()); + auto head = queue + + nodedbg_offset_Environment_HandleWrapQueue__head___ListNode_HandleWrap; + auto next = + head + nodedbg_offset_ListNode_HandleWrap__next___uintptr_t; + next = *reinterpret_cast(next); + + auto expected = reinterpret_cast(&obj); + auto calculated = next - + nodedbg_offset_HandleWrap__handle_wrap_queue___ListNode_HandleWrap; + EXPECT_EQ(expected, calculated); + + obj.persistent().Reset(); // ~HandleWrap() expects an empty handle. +} + +TEST_F(DebugSymbolsTest, ReqWrapList) { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env{handle_scope, argv, this}; + + auto obj_template = v8::FunctionTemplate::New(isolate_); + obj_template->InstanceTemplate()->SetInternalFieldCount(1); + + v8::Local object = + obj_template->GetFunction()->NewInstance(env.context()).ToLocalChecked(); + TestReqWrap obj(*env, object); + + // NOTE (mmarchini): Workaround to fix failing tests on ARM64 machines with + // older GCC. Should be removed once we upgrade the GCC version used on our + // ARM64 CI machinies. + for (auto it : *(*env)->req_wrap_queue()) {} + + auto queue = reinterpret_cast((*env)->req_wrap_queue()); + auto head = queue + + nodedbg_offset_Environment_ReqWrapQueue__head___ListNode_ReqWrapQueue; + auto next = + head + nodedbg_offset_ListNode_ReqWrap__next___uintptr_t; + next = *reinterpret_cast(next); + + auto expected = reinterpret_cast(&obj); + auto calculated = + next - nodedbg_offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue; + EXPECT_EQ(expected, calculated); + + obj.Dispatched(); +} From 7d12ef667901ea3d1f773e073111c607b81dcda8 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 2 Feb 2018 16:25:37 +0100 Subject: [PATCH 45/47] test: fix cctest -Wunused-variable warning PR-URL: https://github.com/nodejs/node/pull/18530 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Joyee Cheung Reviewed-By: Daniel Bevenius --- test/cctest/test_node_postmortem_metadata.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 9ba6e15593a019..72e6abb585f75c 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -130,7 +130,7 @@ TEST_F(DebugSymbolsTest, ReqWrapList) { // NOTE (mmarchini): Workaround to fix failing tests on ARM64 machines with // older GCC. Should be removed once we upgrade the GCC version used on our // ARM64 CI machinies. - for (auto it : *(*env)->req_wrap_queue()) {} + for (auto it : *(*env)->req_wrap_queue()) (void) ⁢ auto queue = reinterpret_cast((*env)->req_wrap_queue()); auto head = queue + From d26e658905184e5e7a1847eecaf86b47ba51daa5 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 9 Feb 2018 03:34:50 +0800 Subject: [PATCH 46/47] src: do not redefine private for GenDebugSymbols Redefining private breaks any private inheritance in the included files. We can simply declare GenDebugSymbols() as friends in related classes to gain the access that we need. PR-URL: https://github.com/nodejs/node/pull/18653 Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- src/base_object.h | 1 + src/env.h | 1 + src/handle_wrap.h | 2 ++ src/node_postmortem_metadata.cc | 54 --------------------------------- src/req_wrap.h | 1 + src/util.h | 2 ++ 6 files changed, 7 insertions(+), 54 deletions(-) diff --git a/src/base_object.h b/src/base_object.h index 5852f764066fbc..965683d029e43e 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -70,6 +70,7 @@ class BaseObject { // offsets and generate debug symbols for BaseObject, which assumes that the // position of members in memory are predictable. For more information please // refer to `doc/guides/node-postmortem-support.md` + friend int GenDebugSymbols(); v8::Persistent persistent_handle_; Environment* env_; }; diff --git a/src/env.h b/src/env.h index b0978db33072df..638753db727136 100644 --- a/src/env.h +++ b/src/env.h @@ -750,6 +750,7 @@ class Environment { // symbols for Environment, which assumes that the position of members in // memory are predictable. For more information please refer to // `doc/guides/node-postmortem-support.md` + friend int GenDebugSymbols(); HandleWrapQueue handle_wrap_queue_; ReqWrapQueue req_wrap_queue_; ListHead&); static void OnClose(uv_handle_t* handle); + // handle_wrap_queue_ needs to be at a fixed offset from the start of the // class because it is used by src/node_postmortem_metadata.cc to calculate // offsets and generate debug symbols for HandleWrap, which assumes that the // position of members in memory are predictable. For more information please // refer to `doc/guides/node-postmortem-support.md` + friend int GenDebugSymbols(); ListNode handle_wrap_queue_; enum { kInitialized, kClosing, kClosingWithCallback, kClosed } state_; uv_handle_t* const handle_; diff --git a/src/node_postmortem_metadata.cc b/src/node_postmortem_metadata.cc index 4a463958f54509..b335e7fbf818c6 100644 --- a/src/node_postmortem_metadata.cc +++ b/src/node_postmortem_metadata.cc @@ -1,57 +1,3 @@ -// Need to import standard headers before redefining private, otherwise it -// won't compile. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace node { -// Forward declaration needed before redefining private. -int GenDebugSymbols(); -} // namespace node - - -#define private friend int GenDebugSymbols(); private - #include "env.h" #include "base_object-inl.h" #include "handle_wrap.h" diff --git a/src/req_wrap.h b/src/req_wrap.h index 05bc558570abf1..ddd0840aad2ab6 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -22,6 +22,7 @@ class ReqWrap : public AsyncWrap { private: friend class Environment; + friend int GenDebugSymbols(); ListNode req_wrap_queue_; protected: diff --git a/src/util.h b/src/util.h index 47bdf27c307109..21c566a4ca6cd6 100644 --- a/src/util.h +++ b/src/util.h @@ -159,6 +159,7 @@ class ListNode { private: template (U::*M)> friend class ListHead; + friend int GenDebugSymbols(); ListNode* prev_; ListNode* next_; DISALLOW_COPY_AND_ASSIGN(ListNode); @@ -189,6 +190,7 @@ class ListHead { inline Iterator end() const; private: + friend int GenDebugSymbols(); ListNode head_; DISALLOW_COPY_AND_ASSIGN(ListHead); }; From 686a66d313aab06dae7d85cee241108190559052 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Mon, 5 Feb 2018 14:57:13 +0100 Subject: [PATCH 47/47] build: add node_lib_target_name to cctest deps Currently the cctest target depend on the node_core_target_name target. But it is the node_lib_target_name target that compiles the sources now which means that if a source file in src is updated the cctest executable will not be re-linked against it, but will remain unchanged. The code will still be compiled, just not linked which means that if you are debugging you'll not see the changes and also a warning will be displayed about this issue. This commit changes the cctest target to depend on node_lib_target_name. PR-URL: https://github.com/nodejs/node/pull/18576 Reviewed-By: Matheus Marchini Reviewed-By: Yihong Wang Reviewed-By: Gibson Fahnestock Reviewed-By: Ben Noordhuis --- node.gyp | 129 +------------------ test/cctest/node_module_reg.cc | 30 ----- test/cctest/test_node_postmortem_metadata.cc | 24 +++- 3 files changed, 25 insertions(+), 158 deletions(-) delete mode 100644 test/cctest/node_module_reg.cc diff --git a/node.gyp b/node.gyp index 673b8fdb7ebd09..7da486ff6d8c7b 100644 --- a/node.gyp +++ b/node.gyp @@ -907,7 +907,7 @@ 'type': 'executable', 'dependencies': [ - '<(node_core_target_name)', + '<(node_lib_target_name)', 'rename_node_bin_win', 'deps/gtest/gtest.gyp:gtest', 'node_js2c#host', @@ -916,39 +916,6 @@ 'node_dtrace_provider', ], - 'variables': { - 'obj_path': '<(obj_dir)/<(node_lib_target_name)/src', - 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)/gen', - 'obj_tracing_path': '<(obj_dir)/<(node_lib_target_name)/src/tracing', - 'obj_suffix': 'o', - 'obj_separator': '/', - 'conditions': [ - ['OS=="win"', { - 'obj_suffix': 'obj', - }], - ['GENERATOR=="ninja"', { - 'obj_path': '<(obj_dir)/src', - 'obj_gen_path': '<(obj_dir)/gen', - 'obj_tracing_path': '<(obj_dir)/src/tracing', - 'obj_separator': '/<(node_lib_target_name).', - }, { - 'conditions': [ - ['OS=="win"', { - 'obj_path': '<(obj_dir)/<(node_lib_target_name)', - 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)', - 'obj_tracing_path': '<(obj_dir)/<(node_lib_target_name)', - }], - ['OS=="aix"', { - 'obj_path': '<(obj_dir)/<(node_lib_target_name)/src', - 'obj_gen_path': '<(obj_dir)/<(node_lib_target_name)/gen', - 'obj_tracing_path': - '<(obj_dir)/<(node_lib_target_name)/src/tracing', - }], - ]} - ] - ], - }, - 'includes': [ 'node.gypi' ], @@ -965,7 +932,6 @@ 'defines': [ 'NODE_WANT_INTERNALS=1' ], 'sources': [ - 'test/cctest/node_module_reg.cc', 'test/cctest/node_test_fixture.cc', 'test/cctest/test_aliased_buffer.cc', 'test/cctest/test_base64.cc', @@ -975,119 +941,30 @@ 'test/cctest/test_url.cc' ], - 'libraries': [ - '<(obj_path)<(obj_separator)async_wrap.<(obj_suffix)', - '<(obj_path)<(obj_separator)handle_wrap.<(obj_suffix)', - '<(obj_path)<(obj_separator)env.<(obj_suffix)', - '<(obj_path)<(obj_separator)node.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_buffer.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_debug_options.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_i18n.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_perf.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_platform.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_url.<(obj_suffix)', - '<(obj_path)<(obj_separator)util.<(obj_suffix)', - '<(obj_path)<(obj_separator)string_bytes.<(obj_suffix)', - '<(obj_path)<(obj_separator)string_search.<(obj_suffix)', - '<(obj_path)<(obj_separator)stream_base.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_constants.<(obj_suffix)', - '<(obj_tracing_path)<(obj_separator)agent.<(obj_suffix)', - '<(obj_tracing_path)<(obj_separator)node_trace_buffer.<(obj_suffix)', - '<(obj_tracing_path)<(obj_separator)node_trace_writer.<(obj_suffix)', - '<(obj_tracing_path)<(obj_separator)trace_event.<(obj_suffix)', - '<(obj_gen_path)<(obj_separator)node_javascript.<(obj_suffix)', - ], - 'conditions': [ [ 'node_use_openssl=="true"', { - 'conditions': [ - ['node_target_type!="static_library"', { - 'libraries': [ - '<(obj_path)<(obj_separator)node_crypto.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_crypto_bio.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_crypto_clienthello.<(obj_suffix)', - '<(obj_path)<(obj_separator)tls_wrap.<(obj_suffix)', - ], - }], - ], 'defines': [ 'HAVE_OPENSSL=1', ], }], [ 'node_use_perfctr=="true"', { 'defines': [ 'HAVE_PERFCTR=1' ], - 'libraries': [ - '<(obj_path)<(obj_separator)node_counters.<(obj_suffix)', - '<(obj_path)<(obj_separator)' - 'node_win32_perfctr_provider.<(obj_suffix)', - ], }], ['v8_enable_inspector==1', { 'sources': [ 'test/cctest/test_inspector_socket.cc', 'test/cctest/test_inspector_socket_server.cc' ], - 'conditions': [ - ['node_target_type!="static_library"', { - 'libraries': [ - '<(obj_path)<(obj_separator)inspector_agent.<(obj_suffix)', - '<(obj_path)<(obj_separator)inspector_io.<(obj_suffix)', - '<(obj_path)<(obj_separator)inspector_js_api.<(obj_suffix)', - '<(obj_path)<(obj_separator)inspector_socket.<(obj_suffix)', - '<(obj_path)<(obj_separator)inspector_socket_server.<(obj_suffix)', - ], - }], - ], 'defines': [ 'HAVE_INSPECTOR=1', ], - }], - [ 'node_use_dtrace=="true" and node_target_type!="static_library"', { - 'libraries': [ - '<(obj_path)<(obj_separator)node_dtrace.<(obj_suffix)', - ], - 'conditions': [ - ['OS!="mac" and OS!="linux"', { - 'libraries': [ - '<(obj_path)<(obj_separator)node_dtrace_provider.<(obj_suffix)', - '<(obj_path)<(obj_separator)node_dtrace_ustack.<(obj_suffix)', - ] - }], - ['OS=="linux"', { - 'libraries': [ - '<(SHARED_INTERMEDIATE_DIR)<(obj_separator)' - 'node_dtrace_provider.<(obj_suffix)', - ] - }], - ], - }, { - 'conditions': [ - [ 'node_use_etw=="true" and OS=="win"', { - 'libraries': [ - '<(obj_path)<(obj_separator)node_dtrace.<(obj_suffix)', - '<(obj_path)<(obj_separator)' - 'node_win32_etw_provider.<(obj_suffix)', - ], - }] - ] - }], - [ 'OS=="win" and node_target_type!="static_library"', { - 'libraries': [ - '<(obj_path)<(obj_separator)backtrace_win32.<(obj_suffix)', - ], }, { - 'conditions': [ - ['node_target_type!="static_library"', { - 'libraries': [ - '<(obj_path)<(obj_separator)backtrace_posix.<(obj_suffix)', - ], - }], - ], + 'defines': [ 'HAVE_INSPECTOR=0' ] }], ['OS=="solaris"', { 'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ] }], - ] + ], } ], # end targets diff --git a/test/cctest/node_module_reg.cc b/test/cctest/node_module_reg.cc deleted file mode 100644 index bd4f20bc9f823d..00000000000000 --- a/test/cctest/node_module_reg.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Need to create empty definition for these modules' -// registration function for cctest. Because when -// building cctest, the definitions for the following -// registration functions are not included. -void _register_cares_wrap() {} -void _register_config() {} -void _register_contextify() {} -void _register_domain() {} -void _register_fs() {} -void _register_fs_event_wrap() {} -void _register_http2() {} -void _register_http_parser() {} -void _register_js_stream() {} -void _register_module_wrap() {} -void _register_os() {} -void _register_pipe_wrap() {} -void _register_process_wrap() {} -void _register_serdes() {} -void _register_signal_wrap() {} -void _register_spawn_sync() {} -void _register_stream_wrap() {} -void _register_tcp_wrap() {} -void _register_timer_wrap() {} -void _register_trace_events() {} -void _register_tty_wrap() {} -void _register_udp_wrap() {} -void _register_util() {} -void _register_uv() {} -void _register_v8() {} -void _register_zlib() {} diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 72e6abb585f75c..e46cb69432d8bf 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -1,5 +1,3 @@ -#include "node_postmortem_metadata.cc" - #include "gtest/gtest.h" #include "node.h" #include "node_internals.h" @@ -7,6 +5,28 @@ #include "req_wrap-inl.h" #include "tracing/agent.h" #include "v8.h" +#include "v8abbr.h" + +extern "C" { +extern uintptr_t + nodedbg_offset_HandleWrap__handle_wrap_queue___ListNode_HandleWrap; +extern uintptr_t + nodedbg_offset_Environment__handle_wrap_queue___Environment_HandleWrapQueue; +extern int debug_symbols_generated; +extern int nodedbg_const_Environment__kContextEmbedderDataIndex__int; +extern uintptr_t + nodedbg_offset_Environment_HandleWrapQueue__head___ListNode_HandleWrap; +extern uintptr_t + nodedbg_offset_Environment__req_wrap_queue___Environment_ReqWrapQueue; +extern uintptr_t nodedbg_offset_ExternalString__data__uintptr_t; +extern uintptr_t nodedbg_offset_ListNode_ReqWrap__next___uintptr_t; +extern uintptr_t nodedbg_offset_ReqWrap__req_wrap_queue___ListNode_ReqWrapQueue; +extern uintptr_t nodedbg_offset_ListNode_HandleWrap__next___uintptr_t; +extern uintptr_t + nodedbg_offset_Environment_ReqWrapQueue__head___ListNode_ReqWrapQueue; +extern uintptr_t + nodedbg_offset_BaseObject__persistent_handle___v8_Persistent_v8_Object; +} class DebugSymbolsTest : public EnvironmentTestFixture {};