Skip to content

Commit

Permalink
feat: Add ModuleLoader::finish_load hook (#987)
Browse files Browse the repository at this point in the history
This commit adds `ModuleLoader::finish_load` hook that is an optional
synchronous function that is called when the module load is done.
  • Loading branch information
bartlomieju authored Dec 4, 2024
1 parent cf34ee3 commit 1682f1a
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 9 deletions.
28 changes: 27 additions & 1 deletion core/modules/loaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ pub trait ModuleLoader {
async { Ok(()) }.boxed_local()
}

/// This hook can be used by implementors to do some cleanup
/// work after loading of modules. The hook is called for
/// all loads, whether they succeeded or not.
///
/// For example implementor might drop transpilation and
/// static analysis caches before
/// yielding control back to the runtime.
///
/// It's not required to implement this method.
fn finish_load(&self) {}

/// Called when new v8 code cache is available for this module. Implementors
/// can store the provided code cache for future executions of the same module.
///
Expand Down Expand Up @@ -460,6 +471,7 @@ pub struct TestingModuleLoader<L: ModuleLoader> {
log: RefCell<Vec<ModuleSpecifier>>,
load_count: std::cell::Cell<usize>,
prepare_count: std::cell::Cell<usize>,
finish_count: std::cell::Cell<usize>,
resolve_count: std::cell::Cell<usize>,
}

Expand All @@ -471,6 +483,7 @@ impl<L: ModuleLoader> TestingModuleLoader<L> {
log: RefCell::new(vec![]),
load_count: Default::default(),
prepare_count: Default::default(),
finish_count: Default::default(),
resolve_count: Default::default(),
}
}
Expand All @@ -480,6 +493,7 @@ impl<L: ModuleLoader> TestingModuleLoader<L> {
ModuleLoadEventCounts {
load: self.load_count.get(),
prepare: self.prepare_count.get(),
finish: self.finish_count.get(),
resolve: self.resolve_count.get(),
}
}
Expand Down Expand Up @@ -509,6 +523,11 @@ impl<L: ModuleLoader> ModuleLoader for TestingModuleLoader<L> {
.prepare_load(module_specifier, maybe_referrer, is_dyn_import)
}

fn finish_load(&self) {
self.finish_count.set(self.finish_count.get() + 1);
self.loader.finish_load();
}

fn load(
&self,
module_specifier: &ModuleSpecifier,
Expand All @@ -532,15 +551,22 @@ impl<L: ModuleLoader> ModuleLoader for TestingModuleLoader<L> {
pub struct ModuleLoadEventCounts {
pub resolve: usize,
pub prepare: usize,
pub finish: usize,
pub load: usize,
}

#[cfg(test)]
impl ModuleLoadEventCounts {
pub fn new(resolve: usize, prepare: usize, load: usize) -> Self {
pub fn new(
resolve: usize,
prepare: usize,
finish: usize,
load: usize,
) -> Self {
Self {
resolve,
prepare,
finish,
load,
}
}
Expand Down
10 changes: 9 additions & 1 deletion core/modules/recursive_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ pub(crate) struct RecursiveModuleLoad {
loader: Rc<dyn ModuleLoader>,
}

impl Drop for RecursiveModuleLoad {
fn drop(&mut self) {
self.loader.finish_load();
}
}

impl RecursiveModuleLoad {
/// Starts a new asynchronous load of the module graph for given specifier.
///
Expand Down Expand Up @@ -310,7 +316,9 @@ impl Stream for RecursiveModuleLoad {
LoadState::Init => {
let module_specifier = match inner.resolve_root() {
Ok(url) => url,
Err(error) => return Poll::Ready(Some(Err(error))),
Err(error) => {
return Poll::Ready(Some(Err(error)));
}
};
let requested_module_type = match &inner.init {
LoadInit::DynamicImport(_, _, module_type) => module_type.clone(),
Expand Down
14 changes: 7 additions & 7 deletions core/modules/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ fn test_mods() {

runtime.instantiate_module(mod_b).unwrap();
assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0);
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(1, 0, 0));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(1, 0, 0, 0));

runtime.instantiate_module(mod_a).unwrap();
assert_eq!(DISPATCH_COUNT.load(Ordering::Relaxed), 0);
Expand Down Expand Up @@ -573,7 +573,7 @@ fn test_json_module() {
};

runtime.instantiate_module(mod_c).unwrap();
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(1, 0, 0));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(1, 0, 0, 0));

runtime.instantiate_module(mod_b).unwrap();

Expand Down Expand Up @@ -1000,7 +1000,7 @@ async fn dyn_import_err() {
// We should get an error here.
let result = runtime.poll_event_loop(cx, Default::default());
assert!(matches!(result, Poll::Ready(Err(_))));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1, 1));
Poll::Ready(())
})
.await;
Expand Down Expand Up @@ -1041,12 +1041,12 @@ async fn dyn_import_ok() {
runtime.poll_event_loop(cx, Default::default()),
Poll::Ready(Ok(_))
));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(5, 1, 1));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(5, 1, 1, 1));
assert!(matches!(
runtime.poll_event_loop(cx, Default::default()),
Poll::Ready(Ok(_))
));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(5, 1, 1));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(5, 1, 1, 1));
Poll::Ready(())
})
.await;
Expand Down Expand Up @@ -1084,10 +1084,10 @@ async fn dyn_import_borrow_mut_error() {
// Old comments that are likely wrong:
// First poll runs `prepare_load` hook.
let _ = runtime.poll_event_loop(cx, Default::default());
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1, 1));
// Second poll triggers error
let _ = runtime.poll_event_loop(cx, Default::default());
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1));
assert_eq!(loader.counts(), ModuleLoadEventCounts::new(4, 1, 1, 1));
Poll::Ready(())
})
.await;
Expand Down

0 comments on commit 1682f1a

Please sign in to comment.