Skip to content

Commit caf608d

Browse files
committed
Implement Web Worker approach to fix signal handler accumulation
The WASM compiler was failing after multiple compilations with "too many signal callbacks" error because LLVM's signal handlers were accumulating in the global scope. Solution: Create a fresh Web Worker for each compilation, which provides a completely isolated JavaScript context. After compilation completes, the worker is terminated, resetting all state including signal handlers. Changes: - Created compiler-worker.js to handle WASM loading and compilation - Modified index.html to use workers instead of direct Module.callMain() - Added polling mechanism to wait for Emscripten FS initialization - Each compilation now runs in a fresh, clean environment This allows unlimited compilations without state buildup issues.
1 parent 56075ed commit caf608d

File tree

2 files changed

+281
-230
lines changed

2 files changed

+281
-230
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Web Worker for running Clang WASM compiler
2+
// This worker is terminated and recreated for each compilation to avoid
3+
// LLVM signal handler registration issues
4+
5+
let isReady = false;
6+
7+
// Handle messages from main thread
8+
self.onmessage = function(e) {
9+
const { type, ...data } = e.data;
10+
11+
if (type === 'load') {
12+
// Configure Module BEFORE loading clang.js
13+
// Emscripten will check if Module exists and merge with it
14+
self.Module = {
15+
print: function(text) {
16+
self.postMessage({ type: 'stdout', text });
17+
},
18+
printErr: function(text) {
19+
self.postMessage({ type: 'stderr', text });
20+
},
21+
noInitialRun: true,
22+
postRun: [function() {
23+
// postRun happens AFTER runtime initialization
24+
console.log('Worker: postRun called');
25+
console.log('Worker: Checking what exists on Module:', Object.keys(self.Module).filter(k => k.startsWith('FS') || k === 'FS'));
26+
27+
// Try different FS locations
28+
const FS = self.Module.FS || self.FS || (typeof FS !== 'undefined' ? FS : null);
29+
console.log('Worker: FS found?', !!FS);
30+
31+
if (FS) {
32+
console.log('Worker: FS.initialized?', FS.initialized);
33+
isReady = true;
34+
self.postMessage({ type: 'ready' });
35+
} else {
36+
// Emscripten might attach FS later, poll for it
37+
let attempts = 0;
38+
const checkFS = setInterval(() => {
39+
attempts++;
40+
const fsNow = self.Module.FS || self.FS || (typeof FS !== 'undefined' ? FS : null);
41+
console.log(`Worker: Polling for FS (attempt ${attempts}):`, !!fsNow);
42+
43+
if (fsNow) {
44+
clearInterval(checkFS);
45+
isReady = true;
46+
console.log('Worker: FS now available, sending ready');
47+
self.postMessage({ type: 'ready' });
48+
} else if (attempts > 100) {
49+
clearInterval(checkFS);
50+
console.error('Worker: Gave up waiting for FS');
51+
self.postMessage({
52+
type: 'error',
53+
error: 'Filesystem never became available'
54+
});
55+
}
56+
}, 100);
57+
}
58+
}]
59+
};
60+
61+
// Load the Emscripten-generated JavaScript
62+
try {
63+
console.log('Worker: Loading script from', data.scriptUrl);
64+
importScripts(data.scriptUrl);
65+
console.log('Worker: Script loaded');
66+
} catch (error) {
67+
console.error('Worker: Failed to load script', error);
68+
self.postMessage({
69+
type: 'error',
70+
error: `Failed to load compiler: ${error.message}`
71+
});
72+
}
73+
} else if (type === 'compile') {
74+
const FS = self.Module?.FS || self.FS;
75+
76+
if (!isReady || !FS) {
77+
self.postMessage({
78+
type: 'error',
79+
error: 'Compiler not ready - FS not available'
80+
});
81+
return;
82+
}
83+
84+
try {
85+
const { code, extraFlags = [] } = data;
86+
const inputFile = 'input.c';
87+
88+
// Write source file
89+
FS.writeFile(inputFile, code);
90+
91+
// Build arguments
92+
const args = [
93+
'-fsyntax-only',
94+
'--target=wasm32-unknown-emscripten',
95+
...extraFlags,
96+
inputFile
97+
];
98+
99+
console.log('Worker compiling with args:', args);
100+
101+
// Run compiler
102+
const exitCode = self.Module.callMain(args);
103+
104+
// Clean up
105+
try {
106+
FS.unlink(inputFile);
107+
} catch (e) {
108+
// Ignore
109+
}
110+
111+
// Send completion
112+
self.postMessage({
113+
type: 'complete',
114+
exitCode
115+
});
116+
117+
} catch (error) {
118+
self.postMessage({
119+
type: 'error',
120+
error: error.message || String(error)
121+
});
122+
}
123+
}
124+
};

0 commit comments

Comments
 (0)