Skip to content

Commit 40a089c

Browse files
authored
Merge pull request webpack#7027 from webpack/bugfix/load-module-race
fix race condition when using loadModule
2 parents 0e88e8a + 985e6fb commit 40a089c

File tree

6 files changed

+66
-31
lines changed

6 files changed

+66
-31
lines changed

lib/dependencies/LoaderPlugin.js

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class LoaderPlugin {
3636
}`
3737
)
3838
);
39+
compilation.semaphore.release();
3940
compilation.addModuleDependencies(
4041
module,
4142
[
@@ -48,37 +49,39 @@ class LoaderPlugin {
4849
"lm",
4950
false,
5051
err => {
51-
if (err) return callback(err);
52+
compilation.semaphore.acquire(() => {
53+
if (err) return callback(err);
5254

53-
if (!dep.module)
54-
return callback(new Error("Cannot load the module"));
55+
if (!dep.module)
56+
return callback(new Error("Cannot load the module"));
5557

56-
if (dep.module.error) return callback(dep.module.error);
57-
if (!dep.module._source)
58-
throw new Error(
59-
"The module created for a LoaderDependency must have a property _source"
60-
);
61-
let source, map;
62-
const moduleSource = dep.module._source;
63-
if (moduleSource.sourceAndMap) {
64-
const sourceAndMap = moduleSource.sourceAndMap();
65-
map = sourceAndMap.map;
66-
source = sourceAndMap.source;
67-
} else {
68-
map = moduleSource.map();
69-
source = moduleSource.source();
70-
}
71-
if (dep.module.buildInfo.fileDependencies) {
72-
for (const d of dep.module.buildInfo.fileDependencies) {
73-
loaderContext.addDependency(d);
58+
if (dep.module.error) return callback(dep.module.error);
59+
if (!dep.module._source)
60+
throw new Error(
61+
"The module created for a LoaderDependency must have a property _source"
62+
);
63+
let source, map;
64+
const moduleSource = dep.module._source;
65+
if (moduleSource.sourceAndMap) {
66+
const sourceAndMap = moduleSource.sourceAndMap();
67+
map = sourceAndMap.map;
68+
source = sourceAndMap.source;
69+
} else {
70+
map = moduleSource.map();
71+
source = moduleSource.source();
7472
}
75-
}
76-
if (dep.module.buildInfo.contextDependencies) {
77-
for (const d of dep.module.buildInfo.contextDependencies) {
78-
loaderContext.addContextDependency(d);
73+
if (dep.module.buildInfo.fileDependencies) {
74+
for (const d of dep.module.buildInfo.fileDependencies) {
75+
loaderContext.addDependency(d);
76+
}
7977
}
80-
}
81-
return callback(null, source, map, dep.module);
78+
if (dep.module.buildInfo.contextDependencies) {
79+
for (const d of dep.module.buildInfo.contextDependencies) {
80+
loaderContext.addContextDependency(d);
81+
}
82+
}
83+
return callback(null, source, map, dep.module);
84+
});
8285
}
8386
);
8487
};

lib/util/Semaphore.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Semaphore {
88
constructor(available) {
99
this.available = available;
1010
this.waiters = [];
11+
this._continue = this._continue.bind(this);
1112
}
1213

1314
acquire(callback) {
@@ -20,11 +21,19 @@ class Semaphore {
2021
}
2122

2223
release() {
24+
this.available++;
2325
if (this.waiters.length > 0) {
24-
const callback = this.waiters.pop();
25-
process.nextTick(callback);
26-
} else {
27-
this.available++;
26+
process.nextTick(this._continue);
27+
}
28+
}
29+
30+
_continue() {
31+
if (this.available > 0) {
32+
if (this.waiters.length > 0) {
33+
this.available--;
34+
const callback = this.waiters.pop();
35+
callback();
36+
}
2837
}
2938
}
3039
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
it("should not deadlock when using loadModule", () => {
2+
const result = require("./loader!");
3+
result.should.match(/console.log\(42\)/);
4+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = function() {
2+
const callback = this.async();
3+
let finished = false;
4+
this.loadModule("./module.js", (err, result) => {
5+
if (err) return callback(err);
6+
if (finished) return;
7+
finished = true;
8+
callback(null, `module.exports = ${JSON.stringify(result)};`);
9+
});
10+
setTimeout(() => {
11+
if (finished) return;
12+
finished = true;
13+
callback(new Error("loadModule is hanging"));
14+
}, 2000);
15+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(42);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
parallelism: 1
3+
};

0 commit comments

Comments
 (0)