Skip to content

Commit

Permalink
feat(ext/node): eagerly bootstrap node (#20153)
Browse files Browse the repository at this point in the history
To fix bugs around detection of when node emulation is required, we will
just eagerly initialize it. The improvements we make to reduce the
impact of the startup time:

 - [x] Process stdin/stdout/stderr are lazily created
 - [x] node.js global proxy no longer allocates on each access check
- [x] Process checks for `beforeExit` listeners before doing expensive
shutdown work
- [x] Process should avoid adding global event handlers until listeners
are added

Benchmarking this PR (`89de7e1ff`) vs main (`41cad2179`)

```
12:36 $ third_party/prebuilt/mac/hyperfine --warmup 100 -S none './deno-41cad2179 run ./empty.js' './deno-89de7e1ff run ./empty.js'
Benchmark 1: ./deno-41cad2179 run ./empty.js
  Time (mean ± σ):      24.3 ms ±   1.6 ms    [User: 16.2 ms, System: 6.0 ms]
  Range (min … max):    21.1 ms …  29.1 ms    115 runs
 
Benchmark 2: ./deno-89de7e1ff run ./empty.js
  Time (mean ± σ):      24.0 ms ±   1.4 ms    [User: 16.3 ms, System: 5.6 ms]
  Range (min … max):    21.3 ms …  28.6 ms    126 runs
```

Fixes #20142
Fixes #15826
Fixes #20028
  • Loading branch information
mmastrac committed Aug 15, 2023
1 parent 41cad21 commit 4380a09
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 399 deletions.
10 changes: 0 additions & 10 deletions cli/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions;
use crate::worker::HasNodeSpecifierChecker;

use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
Expand Down Expand Up @@ -623,7 +622,6 @@ impl CliFactory {
StorageKeyResolver::from_options(&self.options),
self.npm_resolver().await?.clone(),
node_resolver.clone(),
Box::new(CliHasNodeSpecifierChecker(self.graph_container().clone())),
self.blob_store().clone(),
Box::new(CliModuleLoaderFactory::new(
&self.options,
Expand Down Expand Up @@ -683,11 +681,3 @@ impl CliFactory {
})
}
}

struct CliHasNodeSpecifierChecker(Arc<ModuleGraphContainer>);

impl HasNodeSpecifierChecker for CliHasNodeSpecifierChecker {
fn has_node_specifier(&self) -> bool {
self.0.graph().has_node_specifier
}
}
5 changes: 0 additions & 5 deletions cli/npm/resolvers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,6 @@ impl CliNpmResolver {
specifier.as_ref().starts_with(root_dir_url.as_str())
}

/// If the resolver has resolved any npm packages.
pub fn has_packages(&self) -> bool {
self.resolution.has_packages()
}

/// Adds package requirements to the resolver and ensures everything is setup.
pub async fn add_package_reqs(
&self,
Expand Down
10 changes: 0 additions & 10 deletions cli/standalone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use crate::util::progress_bar::ProgressBarStyle;
use crate::util::v8::construct_v8_flags;
use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions;
use crate::worker::HasNodeSpecifierChecker;
use crate::worker::ModuleLoaderFactory;
use deno_ast::MediaType;
use deno_core::anyhow::Context;
Expand Down Expand Up @@ -266,14 +265,6 @@ impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
}
}
struct StandaloneHasNodeSpecifierChecker;
impl HasNodeSpecifierChecker for StandaloneHasNodeSpecifierChecker {
fn has_node_specifier(&self) -> bool {
false
}
}
struct StandaloneRootCertStoreProvider {
ca_stores: Option<Vec<String>>,
ca_data: Option<CaData>,
Expand Down Expand Up @@ -438,7 +429,6 @@ pub async fn run(
StorageKeyResolver::empty(),
npm_resolver.clone(),
node_resolver,
Box::new(StandaloneHasNodeSpecifierChecker),
Default::default(),
Box::new(module_loader_factory),
root_cert_store_provider,
Expand Down
14 changes: 0 additions & 14 deletions cli/tools/repl/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use deno_core::serde_json;
use deno_core::serde_json::Value;
use deno_core::LocalInspectorSession;
use deno_graph::source::Resolver;
use deno_runtime::deno_node;
use deno_runtime::worker::MainWorker;
use deno_semver::npm::NpmPackageReqReference;
use once_cell::sync::Lazy;
Expand Down Expand Up @@ -123,15 +122,13 @@ struct TsEvaluateResponse {
}

pub struct ReplSession {
has_node_modules_dir: bool,
npm_resolver: Arc<CliNpmResolver>,
resolver: Arc<CliGraphResolver>,
pub worker: MainWorker,
session: LocalInspectorSession,
pub context_id: u64,
pub language_server: ReplLanguageServer,
pub notifications: Rc<RefCell<UnboundedReceiver<Value>>>,
has_initialized_node_runtime: bool,
referrer: ModuleSpecifier,
}

Expand Down Expand Up @@ -183,14 +180,12 @@ impl ReplSession {
.unwrap();

let mut repl_session = ReplSession {
has_node_modules_dir: cli_options.has_node_modules_dir(),
npm_resolver,
resolver,
worker,
session,
context_id,
language_server,
has_initialized_node_runtime: false,
referrer,
notifications: Rc::new(RefCell::new(notification_rx)),
};
Expand Down Expand Up @@ -515,15 +510,6 @@ impl ReplSession {
let has_node_specifier =
resolved_imports.iter().any(|url| url.scheme() == "node");
if !npm_imports.is_empty() || has_node_specifier {
if !self.has_initialized_node_runtime {
deno_node::initialize_runtime(
&mut self.worker.js_runtime,
self.has_node_modules_dir,
None,
)?;
self.has_initialized_node_runtime = true;
}

self.npm_resolver.add_package_reqs(&npm_imports).await?;

// prevent messages in the repl about @types/node not being cached
Expand Down
78 changes: 11 additions & 67 deletions cli/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::task::LocalFutureObj;
use deno_core::futures::FutureExt;
use deno_core::located_script_name;
use deno_core::parking_lot::Mutex;
Expand All @@ -32,7 +31,6 @@ use deno_runtime::deno_web::BlobStore;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
use deno_runtime::ops::worker_host::WorkerEventCb;
use deno_runtime::permissions::PermissionsContainer;
use deno_runtime::web_worker::WebWorker;
use deno_runtime::web_worker::WebWorkerOptions;
Expand Down Expand Up @@ -97,7 +95,6 @@ struct SharedWorkerState {
storage_key_resolver: StorageKeyResolver,
npm_resolver: Arc<CliNpmResolver>,
node_resolver: Arc<NodeResolver>,
has_node_specifier_checker: Box<dyn HasNodeSpecifierChecker>,
blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel,
shared_array_buffer_store: SharedArrayBufferStore,
Expand All @@ -110,11 +107,7 @@ struct SharedWorkerState {
}

impl SharedWorkerState {
pub fn should_initialize_node_runtime(&self) -> bool {
self.npm_resolver.has_packages()
|| self.has_node_specifier_checker.has_node_specifier()
|| self.options.is_npm_main
}
// Currently empty
}

pub struct CliMainWorker {
Expand All @@ -140,7 +133,6 @@ impl CliMainWorker {
log::debug!("main_module {}", self.main_module);

if self.is_main_cjs {
self.initialize_main_module_for_node()?;
deno_node::load_cjs_module(
&mut self.worker.js_runtime,
&self.main_module.to_file_path().unwrap().to_string_lossy(),
Expand Down Expand Up @@ -266,22 +258,9 @@ impl CliMainWorker {
&mut self,
id: ModuleId,
) -> Result<(), AnyError> {
if self.shared.should_initialize_node_runtime() {
self.initialize_main_module_for_node()?;
}
self.worker.evaluate_module(id).await
}

fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> {
deno_node::initialize_runtime(
&mut self.worker.js_runtime,
self.shared.options.has_node_modules_dir,
self.shared.options.maybe_binary_npm_command_name.as_deref(),
)?;

Ok(())
}

pub async fn maybe_setup_coverage_collector(
&mut self,
) -> Result<Option<CoverageCollector>, AnyError> {
Expand Down Expand Up @@ -312,7 +291,6 @@ impl CliMainWorkerFactory {
storage_key_resolver: StorageKeyResolver,
npm_resolver: Arc<CliNpmResolver>,
node_resolver: Arc<NodeResolver>,
has_node_specifier_checker: Box<dyn HasNodeSpecifierChecker>,
blob_store: Arc<BlobStore>,
module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
Expand All @@ -327,7 +305,6 @@ impl CliMainWorkerFactory {
storage_key_resolver,
npm_resolver,
node_resolver,
has_node_specifier_checker,
blob_store,
broadcast_channel: Default::default(),
shared_array_buffer_store: Default::default(),
Expand Down Expand Up @@ -404,10 +381,6 @@ impl CliMainWorkerFactory {

let create_web_worker_cb =
create_web_worker_callback(shared.clone(), stdio.clone());
let web_worker_preload_module_cb =
create_web_worker_preload_module_callback(shared);
let web_worker_pre_execute_module_cb =
create_web_worker_pre_execute_module_callback(shared.clone());

let maybe_storage_key = shared
.storage_key_resolver
Expand Down Expand Up @@ -448,6 +421,11 @@ impl CliMainWorkerFactory {
unstable: shared.options.unstable,
user_agent: version::get_user_agent().to_string(),
inspect: shared.options.is_inspecting,
has_node_modules_dir: shared.options.has_node_modules_dir,
maybe_binary_npm_command_name: shared
.options
.maybe_binary_npm_command_name
.clone(),
},
extensions,
startup_snapshot: crate::js::deno_isolate_init(),
Expand All @@ -461,8 +439,6 @@ impl CliMainWorkerFactory {
source_map_getter: maybe_source_map_getter,
format_js_error_fn: Some(Arc::new(format_js_error)),
create_web_worker_cb,
web_worker_preload_module_cb,
web_worker_pre_execute_module_cb,
maybe_inspector_server,
should_break_on_first_statement: shared.options.inspect_brk,
should_wait_for_inspector_session: shared.options.inspect_wait,
Expand Down Expand Up @@ -555,38 +531,6 @@ impl CliMainWorkerFactory {
}
}

// TODO(bartlomieju): this callback could have default value
// and not be required
fn create_web_worker_preload_module_callback(
_shared: &Arc<SharedWorkerState>,
) -> Arc<WorkerEventCb> {
Arc::new(move |worker| {
let fut = async move { Ok(worker) };
LocalFutureObj::new(Box::new(fut))
})
}

fn create_web_worker_pre_execute_module_callback(
shared: Arc<SharedWorkerState>,
) -> Arc<WorkerEventCb> {
Arc::new(move |mut worker| {
let shared = shared.clone();
let fut = async move {
// this will be up to date after pre-load
if shared.should_initialize_node_runtime() {
deno_node::initialize_runtime(
&mut worker.js_runtime,
shared.options.has_node_modules_dir,
None,
)?;
}

Ok(worker)
};
LocalFutureObj::new(Box::new(fut))
})
}

fn create_web_worker_callback(
shared: Arc<SharedWorkerState>,
stdio: deno_runtime::deno_io::Stdio,
Expand All @@ -602,9 +546,6 @@ fn create_web_worker_callback(
shared.module_loader_factory.create_source_map_getter();
let create_web_worker_cb =
create_web_worker_callback(shared.clone(), stdio.clone());
let preload_module_cb = create_web_worker_preload_module_callback(&shared);
let pre_execute_module_cb =
create_web_worker_pre_execute_module_callback(shared.clone());

let extensions = ops::cli_exts(shared.npm_resolver.clone());

Expand Down Expand Up @@ -636,6 +577,11 @@ fn create_web_worker_callback(
unstable: shared.options.unstable,
user_agent: version::get_user_agent().to_string(),
inspect: shared.options.is_inspecting,
has_node_modules_dir: shared.options.has_node_modules_dir,
maybe_binary_npm_command_name: shared
.options
.maybe_binary_npm_command_name
.clone(),
},
extensions,
startup_snapshot: crate::js::deno_isolate_init(),
Expand All @@ -646,8 +592,6 @@ fn create_web_worker_callback(
root_cert_store_provider: Some(shared.root_cert_store_provider.clone()),
seed: shared.options.seed,
create_web_worker_cb,
preload_module_cb,
pre_execute_module_cb,
format_js_error_fn: Some(Arc::new(format_js_error)),
source_map_getter: maybe_source_map_getter,
module_loader,
Expand Down
6 changes: 4 additions & 2 deletions ext/node/global.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.

use std::mem::MaybeUninit;
use std::rc::Rc;

use deno_core::v8;
Expand Down Expand Up @@ -266,13 +267,14 @@ fn current_mode(scope: &mut v8::HandleScope) -> Mode {
let Some(v8_string) = v8::StackTrace::current_script_name_or_source_url(scope) else {
return Mode::Deno;
};
let string = v8_string.to_rust_string_lossy(scope);
let op_state = deno_core::JsRuntime::op_state_from(scope);
let op_state = op_state.borrow();
let Some(node_resolver) = op_state.try_borrow::<Rc<NodeResolver>>() else {
return Mode::Deno;
};
if node_resolver.in_npm_package_with_cache(string) {
let mut buffer = [MaybeUninit::uninit(); 2048];
let str = v8_string.to_rust_cow_lossy(scope, &mut buffer);
if node_resolver.in_npm_package_with_cache(str) {
Mode::Node
} else {
Mode::Deno
Expand Down
24 changes: 0 additions & 24 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::rc::Rc;
use deno_core::error::AnyError;
use deno_core::located_script_name;
use deno_core::op;
use deno_core::serde_json;
use deno_core::serde_v8;
use deno_core::url::Url;
#[allow(unused_imports)]
Expand Down Expand Up @@ -558,29 +557,6 @@ deno_core::extension!(deno_node,
},
);

pub fn initialize_runtime(
js_runtime: &mut JsRuntime,
uses_local_node_modules_dir: bool,
maybe_binary_command_name: Option<&str>,
) -> Result<(), AnyError> {
let argv0 = if let Some(binary_command_name) = maybe_binary_command_name {
serde_json::to_string(binary_command_name)?
} else {
"undefined".to_string()
};
let source_code = format!(
r#"(function loadBuiltinNodeModules(usesLocalNodeModulesDir, argv0) {{
Deno[Deno.internal].node.initialize(
usesLocalNodeModulesDir,
argv0
);
}})({uses_local_node_modules_dir}, {argv0});"#,
);

js_runtime.execute_script(located_script_name!(), source_code.into())?;
Ok(())
}

pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
module: &str,
Expand Down
4 changes: 4 additions & 0 deletions ext/node/polyfills/02_init.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const requireImpl = internals.requireImpl;
import { nodeGlobals } from "ext:deno_node/00_globals.js";
import "node:module";

globalThis.nodeBootstrap = function (usesLocalNodeModulesDir, argv0) {
initialize(usesLocalNodeModulesDir, argv0);
};

let initialized = false;

function initialize(
Expand Down
Loading

0 comments on commit 4380a09

Please sign in to comment.