From fa284395834c8ad9a2cd87d66c18d10049ad7302 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 25 Mar 2022 21:15:39 +0800 Subject: [PATCH] bootstrap: make I/O streams work with user-land snapshot Use the mksnapshot cleanup hooks to release the references to the native streams so that they can be destroyed right before the snapshot is taken, and move the initialization of the global console to pre-execution so that they can be re-initialized during snapshot dehydration. This makes it possible for user-land snapshots to use the I/O during snapshot building. PR-URL: https://github.com/nodejs/node/pull/42466 Reviewed-By: Darshan Sen Reviewed-By: Mohammed Keyvanzadeh Reviewed-By: Khaidi Chu Reviewed-By: Chengzhong Wu --- lib/internal/bootstrap/pre_execution.js | 6 ++++ .../bootstrap/switches/is_main_thread.js | 34 +++++++++++++++++-- lib/internal/console/constructor.js | 6 ++++ lib/internal/console/global.js | 7 +--- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index ae1724a17989da..06cf9d54d68a81 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -121,6 +121,12 @@ function patchProcessObject(expandArgv1) { } } + // We need to initialize the global console here again with process.stdout + // and friends for snapshot deserialization. + const globalConsole = require('internal/console/global'); + const { initializeGlobalConsole } = require('internal/console/constructor'); + initializeGlobalConsole(globalConsole); + // TODO(joyeecheung): most of these should be deprecated and removed, // except some that we need to be able to mutate during run time. addReadOnlyProcessAlias('_eval', '--eval'); diff --git a/lib/internal/bootstrap/switches/is_main_thread.js b/lib/internal/bootstrap/switches/is_main_thread.js index 606da95f21ca8f..b7bd79e09c4acf 100644 --- a/lib/internal/bootstrap/switches/is_main_thread.js +++ b/lib/internal/bootstrap/switches/is_main_thread.js @@ -122,15 +122,34 @@ let stdin; let stdout; let stderr; +let stdoutDestroy; +let stderrDestroy; + +function refreshStdoutOnSigWinch() { + stdout._refreshSize(); +} + +function refreshStderrOnSigWinch() { + stderr._refreshSize(); +} + function getStdout() { if (stdout) return stdout; stdout = createWritableStdioStream(1); stdout.destroySoon = stdout.destroy; // Override _destroy so that the fd is never actually closed. + stdoutDestroy = stdout._destroy; stdout._destroy = dummyDestroy; if (stdout.isTTY) { - process.on('SIGWINCH', () => stdout._refreshSize()); + process.on('SIGWINCH', refreshStdoutOnSigWinch); } + + internalBinding('mksnapshot').cleanups.push(function cleanupStdout() { + stdout._destroy = stdoutDestroy; + stdout.destroy(); + process.removeListener('SIGWINCH', refreshStdoutOnSigWinch); + stdout = undefined; + }); return stdout; } @@ -138,11 +157,18 @@ function getStderr() { if (stderr) return stderr; stderr = createWritableStdioStream(2); stderr.destroySoon = stderr.destroy; + stderrDestroy = stderr._destroy; // Override _destroy so that the fd is never actually closed. stderr._destroy = dummyDestroy; if (stderr.isTTY) { - process.on('SIGWINCH', () => stderr._refreshSize()); + process.on('SIGWINCH', refreshStderrOnSigWinch); } + internalBinding('mksnapshot').cleanups.push(function cleanupStderr() { + stderr._destroy = stderrDestroy; + stderr.destroy(); + process.removeListener('SIGWINCH', refreshStderrOnSigWinch); + stderr = undefined; + }); return stderr; } @@ -229,6 +255,10 @@ function getStdin() { } } + internalBinding('mksnapshot').cleanups.push(function cleanupStdin() { + stdin.destroy(); + stdin = undefined; + }); return stdin; } diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 695a56164b7d84..5ad57be3bed6a8 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -669,9 +669,15 @@ Console.prototype.dirxml = Console.prototype.log; Console.prototype.error = Console.prototype.warn; Console.prototype.groupCollapsed = Console.prototype.group; +function initializeGlobalConsole(globalConsole) { + globalConsole[kBindStreamsLazy](process); + globalConsole[kBindProperties](true, 'auto'); +} + module.exports = { Console, kBindStreamsLazy, kBindProperties, + initializeGlobalConsole, formatTime // exported for tests }; diff --git a/lib/internal/console/global.js b/lib/internal/console/global.js index d6c0c24d529dcc..782a585957f746 100644 --- a/lib/internal/console/global.js +++ b/lib/internal/console/global.js @@ -21,9 +21,7 @@ const { } = primordials; const { - Console, - kBindStreamsLazy, - kBindProperties + Console } = require('internal/console/constructor'); const globalConsole = ObjectCreate({}); @@ -44,9 +42,6 @@ for (const prop of ReflectOwnKeys(Console.prototype)) { ReflectDefineProperty(globalConsole, prop, desc); } -globalConsole[kBindStreamsLazy](process); -globalConsole[kBindProperties](true, 'auto'); - // This is a legacy feature - the Console constructor is exposed on // the global console instance. globalConsole.Console = Console;