0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-14 17:47:35 -05:00

fix(ext/node): allow automatic worker_thread termination (#22647)

Co-authored-by: Matt Mastracci <matthew@mastracci.com>
This commit is contained in:
Satya Rohith 2024-03-13 22:52:25 +05:30 committed by Nathan Whitaker
parent 316339f40e
commit dd88f6a7da
No known key found for this signature in database
11 changed files with 64 additions and 25 deletions

View file

@ -610,6 +610,7 @@ impl CliMainWorkerFactory {
disable_deprecated_api_warning: shared.disable_deprecated_api_warning, disable_deprecated_api_warning: shared.disable_deprecated_api_warning,
verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning, verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning,
future: shared.enable_future_features, future: shared.enable_future_features,
close_on_idle: true,
}, },
extensions: custom_extensions, extensions: custom_extensions,
startup_snapshot: crate::js::deno_isolate_init(), startup_snapshot: crate::js::deno_isolate_init(),
@ -814,6 +815,7 @@ fn create_web_worker_callback(
disable_deprecated_api_warning: shared.disable_deprecated_api_warning, disable_deprecated_api_warning: shared.disable_deprecated_api_warning,
verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning, verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning,
future: false, future: false,
close_on_idle: args.close_on_idle,
}, },
extensions: vec![], extensions: vec![],
startup_snapshot: crate::js::deno_isolate_init(), startup_snapshot: crate::js::deno_isolate_init(),
@ -841,6 +843,8 @@ fn create_web_worker_callback(
stdio: stdio.clone(), stdio: stdio.clone(),
cache_storage_dir, cache_storage_dir,
feature_checker, feature_checker,
strace_ops: shared.options.strace_ops.clone(),
close_on_idle: args.close_on_idle,
maybe_worker_metadata: args.maybe_worker_metadata, maybe_worker_metadata: args.maybe_worker_metadata,
}; };

View file

@ -211,6 +211,7 @@ class NodeWorker extends EventEmitter {
permissions: null, permissions: null,
name: this.#name, name: this.#name,
workerType: "module", workerType: "module",
closeOnIdle: true,
}, },
serializedWorkerMetadata, serializedWorkerMetadata,
); );
@ -413,7 +414,7 @@ internals.__initWorkerThreads = (
>(); >();
parentPort = self as ParentPort; parentPort = self as ParentPort;
if (typeof maybeWorkerMetadata !== "undefined") { if (maybeWorkerMetadata) {
const { 0: metadata, 1: _ } = maybeWorkerMetadata; const { 0: metadata, 1: _ } = maybeWorkerMetadata;
workerData = metadata.workerData; workerData = metadata.workerData;
environmentData = metadata.environmentData; environmentData = metadata.environmentData;

View file

@ -46,6 +46,7 @@ function createWorker(
permissions, permissions,
name, name,
workerType, workerType,
closeOnIdle,
) { ) {
return op_create_worker({ return op_create_worker({
hasSourceCode, hasSourceCode,
@ -54,6 +55,7 @@ function createWorker(
sourceCode, sourceCode,
specifier, specifier,
workerType, workerType,
closeOnIdle,
}); });
} }
@ -75,14 +77,6 @@ function hostRecvMessage(id) {
const privateWorkerRef = Symbol(); const privateWorkerRef = Symbol();
function refWorker(worker) {
worker[privateWorkerRef](true);
}
function unrefWorker(worker) {
worker[privateWorkerRef](false);
}
class Worker extends EventTarget { class Worker extends EventTarget {
#id = 0; #id = 0;
#name = ""; #name = "";
@ -134,8 +128,9 @@ class Worker extends EventTarget {
hasSourceCode, hasSourceCode,
sourceCode, sourceCode,
deno?.permissions, deno?.permissions,
name, this.#name,
workerType, workerType,
false,
); );
this.#id = id; this.#id = id;
this.#pollControl(); this.#pollControl();
@ -325,4 +320,4 @@ webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
"module", "module",
]); ]);
export { refWorker, unrefWorker, Worker }; export { Worker };

View file

@ -279,6 +279,7 @@ function postMessage(message, transferOrOptions = {}) {
let isClosing = false; let isClosing = false;
let globalDispatchEvent; let globalDispatchEvent;
let closeOnIdle;
async function pollForMessages() { async function pollForMessages() {
if (!globalDispatchEvent) { if (!globalDispatchEvent) {
@ -288,7 +289,14 @@ async function pollForMessages() {
); );
} }
while (!isClosing) { while (!isClosing) {
const data = await op_worker_recv_message(); const op = op_worker_recv_message();
// In a Node.js worker, unref() the op promise to prevent it from
// keeping the event loop alive. This avoids the need to explicitly
// call self.close() or worker.terminate().
if (closeOnIdle) {
core.unrefOpPromise(op);
}
const data = await op;
if (data === null) break; if (data === null) break;
const v = messagePort.deserializeJsMessageData(data); const v = messagePort.deserializeJsMessageData(data);
const message = v[0]; const message = v[0];
@ -803,6 +811,8 @@ function bootstrapWorkerRuntime(
6: argv0, 6: argv0,
7: shouldDisableDeprecatedApiWarning, 7: shouldDisableDeprecatedApiWarning,
8: shouldUseVerboseDeprecatedApiWarning, 8: shouldUseVerboseDeprecatedApiWarning,
9: _future,
10: closeOnIdle_,
} = runtimeOptions; } = runtimeOptions;
deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning; deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
@ -864,6 +874,7 @@ function bootstrapWorkerRuntime(
location.setLocationHref(location_); location.setLocationHref(location_);
closeOnIdle = closeOnIdle_;
globalThis.pollForMessages = pollForMessages; globalThis.pollForMessages = pollForMessages;
// TODO(bartlomieju): deprecate --unstable // TODO(bartlomieju): deprecate --unstable

View file

@ -35,6 +35,7 @@ pub struct CreateWebWorkerArgs {
pub permissions: PermissionsContainer, pub permissions: PermissionsContainer,
pub main_module: ModuleSpecifier, pub main_module: ModuleSpecifier,
pub worker_type: WebWorkerType, pub worker_type: WebWorkerType,
pub close_on_idle: bool,
pub maybe_worker_metadata: Option<JsMessageData>, pub maybe_worker_metadata: Option<JsMessageData>,
} }
@ -114,6 +115,7 @@ pub struct CreateWorkerArgs {
source_code: String, source_code: String,
specifier: String, specifier: String,
worker_type: WebWorkerType, worker_type: WebWorkerType,
close_on_idle: bool,
} }
/// Create worker as the host /// Create worker as the host
@ -191,6 +193,7 @@ fn op_create_worker(
permissions: worker_permissions, permissions: worker_permissions,
main_module: module_specifier.clone(), main_module: module_specifier.clone(),
worker_type, worker_type,
close_on_idle: args.close_on_idle,
maybe_worker_metadata, maybe_worker_metadata,
}); });

View file

@ -5,6 +5,7 @@ use crate::permissions::PermissionsContainer;
use crate::shared::maybe_transpile_source; use crate::shared::maybe_transpile_source;
use crate::shared::runtime; use crate::shared::runtime;
use crate::tokio_util::create_and_run_current_thread; use crate::tokio_util::create_and_run_current_thread;
use crate::worker::create_op_metrics;
use crate::worker::import_meta_resolve_callback; use crate::worker::import_meta_resolve_callback;
use crate::worker::validate_import_attributes_callback; use crate::worker::validate_import_attributes_callback;
use crate::worker::FormatJsErrorFn; use crate::worker::FormatJsErrorFn;
@ -34,7 +35,6 @@ use deno_core::ModuleCodeString;
use deno_core::ModuleId; use deno_core::ModuleId;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::OpMetricsSummaryTracker;
use deno_core::PollEventLoopOptions; use deno_core::PollEventLoopOptions;
use deno_core::RuntimeOptions; use deno_core::RuntimeOptions;
use deno_core::SharedArrayBufferStore; use deno_core::SharedArrayBufferStore;
@ -327,6 +327,7 @@ pub struct WebWorker {
id: WorkerId, id: WorkerId,
pub js_runtime: JsRuntime, pub js_runtime: JsRuntime,
pub name: String, pub name: String,
close_on_idle: bool,
internal_handle: WebWorkerInternalHandle, internal_handle: WebWorkerInternalHandle,
pub worker_type: WebWorkerType, pub worker_type: WebWorkerType,
pub main_module: ModuleSpecifier, pub main_module: ModuleSpecifier,
@ -359,6 +360,8 @@ pub struct WebWorkerOptions {
pub cache_storage_dir: Option<std::path::PathBuf>, pub cache_storage_dir: Option<std::path::PathBuf>,
pub stdio: Stdio, pub stdio: Stdio,
pub feature_checker: Arc<FeatureChecker>, pub feature_checker: Arc<FeatureChecker>,
pub strace_ops: Option<Vec<String>>,
pub close_on_idle: bool,
pub maybe_worker_metadata: Option<JsMessageData>, pub maybe_worker_metadata: Option<JsMessageData>,
} }
@ -511,17 +514,11 @@ impl WebWorker {
#[cfg(feature = "only_snapshotted_js_sources")] #[cfg(feature = "only_snapshotted_js_sources")]
options.startup_snapshot.as_ref().expect("A user snapshot was not provided, even though 'only_snapshotted_js_sources' is used."); options.startup_snapshot.as_ref().expect("A user snapshot was not provided, even though 'only_snapshotted_js_sources' is used.");
// Hook up the summary metrics if the user or subcommand requested them // Get our op metrics
let (op_summary_metrics, op_metrics_factory_fn) = let (op_summary_metrics, op_metrics_factory_fn) = create_op_metrics(
if options.bootstrap.enable_op_summary_metrics { options.bootstrap.enable_op_summary_metrics,
let op_summary_metrics = Rc::new(OpMetricsSummaryTracker::default()); options.strace_ops,
( );
Some(op_summary_metrics.clone()),
Some(op_summary_metrics.op_metrics_factory_fn(|_| true)),
)
} else {
(None, None)
};
let mut js_runtime = JsRuntime::new(RuntimeOptions { let mut js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()), module_loader: Some(options.module_loader.clone()),
@ -606,6 +603,7 @@ impl WebWorker {
main_module, main_module,
poll_for_messages_fn: None, poll_for_messages_fn: None,
bootstrap_fn_global: Some(bootstrap_fn_global), bootstrap_fn_global: Some(bootstrap_fn_global),
close_on_idle: options.close_on_idle,
maybe_worker_metadata: options.maybe_worker_metadata, maybe_worker_metadata: options.maybe_worker_metadata,
}, },
external_handle, external_handle,
@ -759,6 +757,10 @@ impl WebWorker {
return Poll::Ready(Err(e)); return Poll::Ready(Err(e));
} }
if self.close_on_idle {
return Poll::Ready(Ok(()));
}
// TODO(mmastrac): we don't want to test this w/classic workers because // TODO(mmastrac): we don't want to test this w/classic workers because
// WPT triggers a failure here. This is only exposed via --enable-testing-features-do-not-use. // WPT triggers a failure here. This is only exposed via --enable-testing-features-do-not-use.
if self.worker_type == WebWorkerType::Module { if self.worker_type == WebWorkerType::Module {

View file

@ -227,7 +227,7 @@ impl Default for WorkerOptions {
} }
} }
fn create_op_metrics( pub fn create_op_metrics(
enable_op_summary_metrics: bool, enable_op_summary_metrics: bool,
strace_ops: Option<Vec<String>>, strace_ops: Option<Vec<String>>,
) -> ( ) -> (

View file

@ -63,6 +63,7 @@ pub struct BootstrapOptions {
pub disable_deprecated_api_warning: bool, pub disable_deprecated_api_warning: bool,
pub verbose_deprecated_api_warning: bool, pub verbose_deprecated_api_warning: bool,
pub future: bool, pub future: bool,
pub close_on_idle: bool,
} }
impl Default for BootstrapOptions { impl Default for BootstrapOptions {
@ -94,6 +95,7 @@ impl Default for BootstrapOptions {
disable_deprecated_api_warning: false, disable_deprecated_api_warning: false,
verbose_deprecated_api_warning: false, verbose_deprecated_api_warning: false,
future: false, future: false,
close_on_idle: false,
} }
} }
} }
@ -129,6 +131,8 @@ struct BootstrapV8<'a>(
bool, bool,
// future // future
bool, bool,
// close_on_idle
bool,
); );
impl BootstrapOptions { impl BootstrapOptions {
@ -151,6 +155,7 @@ impl BootstrapOptions {
self.disable_deprecated_api_warning, self.disable_deprecated_api_warning,
self.verbose_deprecated_api_warning, self.verbose_deprecated_api_warning,
self.future, self.future,
self.close_on_idle,
); );
bootstrap.serialize(ser).unwrap() bootstrap.serialize(ser).unwrap()

View file

@ -111,3 +111,10 @@ itest!(worker_doest_stall_event_loop {
output: "workers/worker_doest_stall_event_loop.ts.out", output: "workers/worker_doest_stall_event_loop.ts.out",
exit_code: 0, exit_code: 0,
}); });
// Test for https://github.com/denoland/deno/issues/22629
itest!(node_worker_auto_exits {
args: "run --quiet --allow-read workers/node_worker_auto_exits.mjs",
output: "workers/node_worker_auto_exits.mjs.out",
exit_code: 0,
});

View file

@ -0,0 +1,9 @@
import { isMainThread, Worker } from "node:worker_threads";
if (isMainThread) {
// This re-loads the current file inside a Worker instance.
const w = new Worker(import.meta.filename);
} else {
console.log("Inside Worker!");
console.log(isMainThread); // Prints 'false'.
}

View file

@ -0,0 +1,2 @@
Inside Worker!
false