Skip to content

Commit

Permalink
fix: introduce AsyncTask, make rebuildModule safer (#9244)
Browse files Browse the repository at this point in the history
fix: introduce AsyncTask to make rebuildModule safer and correct the order of invoking module_executor hook_after_make
  • Loading branch information
JSerFeng authored Feb 11, 2025
1 parent 3fa4f3e commit 4d4dddd
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 34 deletions.
1 change: 0 additions & 1 deletion crates/rspack_binding_values/src/compilation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,6 @@ impl JsCompilation {
)
.await
.map_err(|e| Error::new(napi::Status::GenericFailure, format!("{e}")))?;

modules
.iter_mut()
.for_each(|module| module.attach(compilation));
Expand Down
15 changes: 8 additions & 7 deletions crates/rspack_core/src/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1201,13 +1201,6 @@ impl Compilation {

self.in_finish_make.store(false, Ordering::Release);

// sync assets to compilation from module_executor
if let Some(module_executor) = &mut self.module_executor {
let mut module_executor = std::mem::take(module_executor);
module_executor.hook_after_finish_modules(self).await;
self.module_executor = Some(module_executor);
}

// take built_modules
if let Some(mutations) = self.incremental.mutations_write() {
mutations.extend(
Expand Down Expand Up @@ -1239,6 +1232,14 @@ impl Compilation {
.finish_modules
.call(self)
.await?;

// sync assets to compilation from module_executor
if let Some(module_executor) = &mut self.module_executor {
let mut module_executor = std::mem::take(module_executor);
module_executor.hook_after_finish_modules(self).await;
self.module_executor = Some(module_executor);
}

logger.time_end(start);
// Collect dependencies diagnostics at here to make sure:
// 1. after finish_modules: has provide exports info
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { a as a1 } from "./a?1";
import { a as a2 } from "./a?2";
import { a as a3 } from "./a?3";

it("compilation rebuild module should works", () => {
expect(a1 + a2 + a3).toBe(15)
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
let times = 0;
module.exports = function loader(content) {
times++;
return content.replace("1", times);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const pluginName = "plugin";

class Plugin {
apply(compiler) {
let initial = true;
compiler.hooks.compilation.tap(pluginName, compilation => {
compilation.hooks.finishModules.tapPromise(pluginName, async modules => {
modules = [...modules];
if (initial) {
initial = false;

const results = await Promise.all(
modules.map(m => {
return new Promise((resolve, reject) => {
compilation.rebuildModule(m, (err, module) => {
if (err) {
reject(err);
} else {
resolve(module);
}
});
});
})
);

// should compile success
expect(results.length).toBe(4);
}
});
});
}
}

/**@type {import("@rspack/core").Configuration}*/
module.exports = {
module: {
rules: [
{
test: /a\.js$/,
use: [
{
loader: "./loader"
}
]
}
]
},
plugins: [new Plugin()]
};
2 changes: 1 addition & 1 deletion packages/rspack/etc/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ export class Compilation {
// (undocumented)
static PROCESS_ASSETS_STAGE_SUMMARIZE: number;
// (undocumented)
rebuildModule(m: Module, f: (err: Error, m: Module) => void): void;
rebuildModule(m: Module, f: (err: Error | null, m: Module | null) => void): void;
// (undocumented)
renameAsset(filename: string, newFilename: string): void;
// (undocumented)
Expand Down
49 changes: 26 additions & 23 deletions packages/rspack/src/Compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
type ExternalObject,
type JsCompatSourceOwned,
type JsCompilation,
type JsModule,
type JsPathData,
JsRspackSeverity,
type JsRuntimeModule
Expand Down Expand Up @@ -53,7 +52,7 @@ import { LogType, Logger } from "./logging/Logger";
import { StatsFactory } from "./stats/StatsFactory";
import { StatsPrinter } from "./stats/StatsPrinter";
import { type AssetInfo, JsAssetInfo } from "./util/AssetInfo";
import MergeCaller from "./util/MergeCaller";
import { AsyncTask } from "./util/AsyncTask";
import { createReadonlyMap } from "./util/createReadonlyMap";
import { createFakeCompilationDependencies } from "./util/fake";
import type { InputFileSystem } from "./util/fs";
Expand Down Expand Up @@ -1075,27 +1074,31 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
);
}

#rebuildModuleCaller = ((compilation: Compilation) =>
new MergeCaller(
(args: Array<[string, (err: Error, m: Module) => void]>) => {
compilation.#inner.rebuildModule(
args.map(item => item[0]),
(err: Error, modules: JsModule[]) => {
for (const [id, callback] of args) {
const m = modules.find(item => item.moduleIdentifier === id);
if (m) {
callback(err, Module.__from_binding(m));
} else {
callback(err || new Error("module no found"), null as any);
}
}
#rebuildModuleTask = new AsyncTask<string, Module>(
(moduleIdentifiers, doneWork) => {
this.#inner.rebuildModule(
moduleIdentifiers,
(err: Error | null, modules: binding.JsModule[]) => {
/*
* TODO:
* batch all call parameters, once a module is failed, we cannot know which module
* is failed to rebuild, we have to make all modules failed, this should be improved
* in the future
*/
if (err) {
doneWork(new Array(moduleIdentifiers.length).fill([err, null]));
} else {
doneWork(
modules.map(jsModule => [null, Module.__from_binding(jsModule)])
);
}
);
}
))(this);
}
);
}
);

rebuildModule(m: Module, f: (err: Error, m: Module) => void) {
this.#rebuildModuleCaller.push([m.identifier(), f]);
rebuildModule(m: Module, f: (err: Error | null, m: Module | null) => void) {
this.#rebuildModuleTask.exec(m.identifier(), f);
}

addRuntimeModule(chunk: Chunk, runtimeModule: RuntimeModule) {
Expand Down Expand Up @@ -1248,7 +1251,7 @@ class AddIncludeDispatcher {
this.#cbs = [];
this.#inner(args, (wholeErr, results) => {
if (this.#args.length !== 0) {
queueMicrotask(this.#execute);
queueMicrotask(this.#execute.bind(this));
}

if (wholeErr) {
Expand Down Expand Up @@ -1281,7 +1284,7 @@ class AddIncludeDispatcher {
callback: (err?: null | WebpackError, module?: Module) => void
) {
if (this.#args.length === 0) {
queueMicrotask(this.#execute);
queueMicrotask(this.#execute.bind(this));
}

this.#args.push([context, dependency, options as any]);
Expand Down
52 changes: 52 additions & 0 deletions packages/rspack/src/util/AsyncTask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
type TaskCallback<Ret> = (err: Error | null, ret: Ret | null) => void;

export class AsyncTask<Param, Ret> {
#isRunning = false;
#params: Param[] = [];
#callbacks: TaskCallback<Ret>[] = [];

#task: (
param: Param[],
callback: (results: [Error | null, Ret | null][]) => void
) => void;

constructor(
task: (
param: Param[],
callback: (results: [Error | null, Ret | null][]) => void
) => void
) {
this.#task = task;
}

#exec_internal() {
const params = this.#params;
const callbacks = this.#callbacks;
this.#params = [];
this.#callbacks = [];

this.#task(params, results => {
this.#isRunning = false;
if (this.#params.length) {
this.#isRunning = true;
queueMicrotask(() => this.#exec_internal());
}

for (let i = 0; i < results.length; i++) {
const [err, result] = results[i];
const callback = callbacks[i];
callback(err, result);
}
});
}

exec(param: Param, callback: TaskCallback<Ret>) {
if (!this.#isRunning) {
queueMicrotask(() => this.#exec_internal());
this.#isRunning = true;
}

this.#params.push(param);
this.#callbacks.push(callback);
}
}
2 changes: 1 addition & 1 deletion website/docs/en/api/javascript-api/compilation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ Triggers a re-build of the module.
```ts
rebuildModule(
m: Module, // module to be rebuilt
f: (err: Error, m: Module) => void // function to be invoked when the module finishes rebuilding
f: (err: Error | null, m: Module | null) => void // function to be invoked when the module finishes rebuilding
): void;
```
Expand Down
2 changes: 1 addition & 1 deletion website/docs/zh/api/javascript-api/compilation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ export default {
重新构建指定模块。
```ts
rebuildModule(m: Module, f: (err: Error, m: Module) => void): void;
rebuildModule(m: Module, f: (err: Error | null, m: Module | null) => void): void;
```
以下示例将重新构建结尾为 `a.js` 模块:
Expand Down

2 comments on commit 4d4dddd

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on 4d4dddd Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2025-02-11 9d82335) Current Change
10000_big_production-mode_disable-minimize + exec 37.7 s ± 637 ms 39.2 s ± 1.07 s +4.02 %
10000_development-mode + exec 1.88 s ± 89 ms 1.87 s ± 165 ms -0.60 %
10000_development-mode_hmr + exec 685 ms ± 7.4 ms 693 ms ± 29 ms +1.12 %
10000_production-mode + exec 2.28 s ± 51 ms 2.3 s ± 117 ms +1.00 %
10000_production-mode_persistent-cold + exec 2.47 s ± 72 ms 2.43 s ± 45 ms -1.40 %
10000_production-mode_persistent-hot + exec 1.65 s ± 61 ms 1.67 s ± 65 ms +1.47 %
arco-pro_development-mode + exec 1.78 s ± 145 ms 1.76 s ± 102 ms -0.90 %
arco-pro_development-mode_hmr + exec 389 ms ± 2.1 ms 387 ms ± 1.3 ms -0.42 %
arco-pro_production-mode + exec 3.59 s ± 217 ms 3.7 s ± 98 ms +3.00 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.72 s ± 229 ms 3.73 s ± 145 ms +0.38 %
arco-pro_production-mode_persistent-cold + exec 3.85 s ± 164 ms 3.81 s ± 132 ms -1.13 %
arco-pro_production-mode_persistent-hot + exec 2.36 s ± 99 ms 2.4 s ± 196 ms +1.86 %
arco-pro_production-mode_traverse-chunk-modules + exec 3.63 s ± 69 ms 3.75 s ± 212 ms +3.25 %
large-dyn-imports_development-mode + exec 2.09 s ± 24 ms 2.11 s ± 65 ms +0.83 %
large-dyn-imports_production-mode + exec 2.15 s ± 71 ms 2.15 s ± 58 ms -0.13 %
threejs_development-mode_10x + exec 1.54 s ± 25 ms 1.54 s ± 21 ms +0.35 %
threejs_development-mode_10x_hmr + exec 779 ms ± 7 ms 786 ms ± 8.1 ms +0.89 %
threejs_production-mode_10x + exec 5.2 s ± 32 ms 5.27 s ± 187 ms +1.34 %
threejs_production-mode_10x_persistent-cold + exec 5.27 s ± 65 ms 5.45 s ± 448 ms +3.37 %
threejs_production-mode_10x_persistent-hot + exec 4.53 s ± 220 ms 4.57 s ± 155 ms +0.87 %
10000_big_production-mode_disable-minimize + rss memory 8706 MiB ± 32.4 MiB 8704 MiB ± 86.6 MiB -0.02 %
10000_development-mode + rss memory 649 MiB ± 12.6 MiB 657 MiB ± 47.2 MiB +1.27 %
10000_development-mode_hmr + rss memory 1344 MiB ± 112 MiB 1315 MiB ± 240 MiB -2.17 %
10000_production-mode + rss memory 621 MiB ± 21.8 MiB 650 MiB ± 37.2 MiB +4.69 %
10000_production-mode_persistent-cold + rss memory 746 MiB ± 24.7 MiB 746 MiB ± 33.9 MiB -0.02 %
10000_production-mode_persistent-hot + rss memory 717 MiB ± 23.3 MiB 752 MiB ± 11.2 MiB +4.85 %
arco-pro_development-mode + rss memory 569 MiB ± 17.5 MiB 590 MiB ± 21.9 MiB +3.68 %
arco-pro_development-mode_hmr + rss memory 643 MiB ± 49.1 MiB 688 MiB ± 32.5 MiB +7.04 %
arco-pro_production-mode + rss memory 714 MiB ± 28.8 MiB 724 MiB ± 44.5 MiB +1.37 %
arco-pro_production-mode_generate-package-json-webpack-plugin + rss memory 732 MiB ± 22.4 MiB 747 MiB ± 12.9 MiB +2.00 %
arco-pro_production-mode_persistent-cold + rss memory 853 MiB ± 45.5 MiB 861 MiB ± 34 MiB +0.88 %
arco-pro_production-mode_persistent-hot + rss memory 708 MiB ± 27.8 MiB 756 MiB ± 25.3 MiB +6.76 %
arco-pro_production-mode_traverse-chunk-modules + rss memory 727 MiB ± 48.2 MiB 737 MiB ± 26.6 MiB +1.34 %
large-dyn-imports_development-mode + rss memory 644 MiB ± 5.26 MiB 663 MiB ± 3.89 MiB +2.89 %
large-dyn-imports_production-mode + rss memory 526 MiB ± 7.84 MiB 551 MiB ± 8.79 MiB +4.68 %
threejs_development-mode_10x + rss memory 553 MiB ± 16.3 MiB 561 MiB ± 16.2 MiB +1.40 %
threejs_development-mode_10x_hmr + rss memory 1145 MiB ± 140 MiB 1110 MiB ± 143 MiB -3.01 %
threejs_production-mode_10x + rss memory 828 MiB ± 50.8 MiB 838 MiB ± 51.9 MiB +1.19 %
threejs_production-mode_10x_persistent-cold + rss memory 960 MiB ± 14.5 MiB 965 MiB ± 49.8 MiB +0.56 %
threejs_production-mode_10x_persistent-hot + rss memory 877 MiB ± 54.8 MiB 846 MiB ± 24 MiB -3.58 %

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented on 4d4dddd Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ecosystem CI detail: Open

suite result
modernjs ❌ failure
rspress ✅ success
rslib ✅ success
rsbuild ❌ failure
rsdoctor ❌ failure
examples ✅ success
devserver ✅ success
nuxt ✅ success

Please sign in to comment.