Skip to content

Commit 8d3f65e

Browse files
committed
fix: prevent rebuild hang
1 parent 8db8d06 commit 8d3f65e

2 files changed

Lines changed: 69 additions & 10 deletions

File tree

src/lib/bundle/index.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,31 @@ import type { BundleWorkerRequest, BundleWorkerResponse } from "./protocol";
33

44
let nextRequestId = 0;
55
let bundleWorker: Worker | null = null;
6+
const bundleRequestTimeoutMs = 60_000;
67

78
const pendingRequests = new Map<
89
number,
910
{
1011
resolve: (result: BundleResult) => void;
1112
reject: (error: Error) => void;
13+
timeout: ReturnType<typeof setTimeout>;
1214
}
1315
>();
1416

1517
function rejectPendingRequests(error: Error) {
16-
for (const { reject } of pendingRequests.values()) {
18+
for (const { reject, timeout } of pendingRequests.values()) {
19+
clearTimeout(timeout);
1720
reject(error);
1821
}
1922
pendingRequests.clear();
2023
}
2124

25+
function resetWorker(error: Error) {
26+
rejectPendingRequests(error);
27+
bundleWorker?.terminate();
28+
bundleWorker = null;
29+
}
30+
2231
function ensureWorker() {
2332
if (bundleWorker) {
2433
return bundleWorker;
@@ -34,6 +43,7 @@ function ensureWorker() {
3443
}
3544

3645
pendingRequests.delete(response.id);
46+
clearTimeout(pending.timeout);
3747
if (response.ok) {
3848
pending.resolve(response.result);
3949
return;
@@ -44,9 +54,7 @@ function ensureWorker() {
4454

4555
bundleWorker.onerror = (event) => {
4656
const error = new Error(event.message || "Rspack worker crashed");
47-
rejectPendingRequests(error);
48-
bundleWorker?.terminate();
49-
bundleWorker = null;
57+
resetWorker(error);
5058
};
5159

5260
return bundleWorker;
@@ -63,9 +71,18 @@ export async function bundle(files: SourceFile[], version: string): Promise<Bund
6371
};
6472

6573
return new Promise((resolve, reject) => {
74+
const timeout = setTimeout(() => {
75+
if (!pendingRequests.has(id)) {
76+
return;
77+
}
78+
79+
resetWorker(new Error("Rspack worker timed out while bundling"));
80+
}, bundleRequestTimeoutMs);
81+
6682
pendingRequests.set(id, {
6783
resolve,
6884
reject,
85+
timeout,
6986
});
7087
worker.postMessage(request);
7188
});

src/lib/bundle/worker.ts

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,65 @@
11
import { bundle } from "./core";
22
import type { BundleWorkerRequest, BundleWorkerResponse } from "./protocol";
33

4-
self.onmessage = async (event: MessageEvent<BundleWorkerRequest>) => {
5-
const { id, files, version } = event.data;
4+
async function handleBundleRequest({ id, files, version }: BundleWorkerRequest) {
5+
let response: BundleWorkerResponse;
66

77
try {
88
const result = await bundle(files, version);
9-
const response: BundleWorkerResponse = {
9+
response = {
1010
id,
1111
ok: true,
1212
result,
1313
};
14-
self.postMessage(response);
1514
} catch (error) {
16-
const response: BundleWorkerResponse = {
15+
response = {
1716
id,
1817
ok: false,
1918
error: error instanceof Error ? error.message : "Failed to bundle with rspack",
2019
};
21-
self.postMessage(response);
2220
}
21+
self.postMessage(response);
22+
}
23+
24+
function rejectSupersededRequest({ id }: BundleWorkerRequest) {
25+
const response: BundleWorkerResponse = {
26+
id,
27+
ok: false,
28+
error: "Bundle request superseded by a newer request",
29+
};
30+
self.postMessage(response);
31+
}
32+
33+
let isBundling = false;
34+
let queuedRequest: BundleWorkerRequest | null = null;
35+
36+
async function drainQueue() {
37+
if (isBundling) {
38+
return;
39+
}
40+
41+
isBundling = true;
42+
try {
43+
while (queuedRequest) {
44+
const request = queuedRequest;
45+
queuedRequest = null;
46+
await handleBundleRequest(request);
47+
}
48+
} finally {
49+
isBundling = false;
50+
if (queuedRequest) {
51+
void drainQueue();
52+
}
53+
}
54+
}
55+
56+
self.onmessage = (event: MessageEvent<BundleWorkerRequest>) => {
57+
const supersededRequest = queuedRequest;
58+
const request = event.data;
59+
60+
queuedRequest = request;
61+
if (supersededRequest) {
62+
rejectSupersededRequest(supersededRequest);
63+
}
64+
void drainQueue();
2365
};

0 commit comments

Comments
 (0)