Skip to content

Commit 60e44c6

Browse files
unamedkrclaude
andauthored
fix(wasm): use ccall({async:true}) — THE fix for ASYNCIFY streaming (#34)
Root cause of all WASM hanging: Module._wasm_generate_async() is a DIRECT function call that does NOT return a Promise even with ASYNCIFY. Only Module.ccall('fn', ..., {async: true}) makes Emscripten create the async wrapper that properly unwinds/rewinds the C stack. The try/catch was silently falling through to Module._wasm_generate() (the sync version), blocking the entire main thread. Fix: replace Module._wasm_generate_async() with Module.ccall() using {async: true}. Add ccall/cwrap to EXPORTED_RUNTIME_METHODS. This is the documented Emscripten ASYNCIFY pattern: https://emscripten.org/docs/porting/asyncify.html#making-async-web-apis-behave-as-if-they-were-synchronous Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 01e0a2d commit 60e44c6

4 files changed

Lines changed: 18 additions & 6 deletions

File tree

wasm/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ emcc "$SCRIPT_DIR/quant_wasm.c" \
2727
-s MAXIMUM_MEMORY=4GB \
2828
-s INITIAL_MEMORY=256MB \
2929
-s EXPORTED_FUNCTIONS='["_main","_wasm_load_model","_wasm_generate","_wasm_generate_async","_wasm_model_info","_wasm_is_ready","_malloc","_free"]' \
30-
-s EXPORTED_RUNTIME_METHODS='["UTF8ToString","allocateUTF8","FS"]' \
30+
-s EXPORTED_RUNTIME_METHODS='["UTF8ToString","allocateUTF8","FS","ccall","cwrap"]' \
3131
-s FORCE_FILESYSTEM=1 \
3232
-s MODULARIZE=0 \
3333
-s ENVIRONMENT=web \

wasm/index.html

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,22 @@ <h2>Run an <span>LLM</span> in your browser</h2>
433433

434434
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
435435

436-
const ptr = Module.allocateUTF8(getChatPrompt(text));
437-
try { await Module._wasm_generate_async(ptr, 0.7, 256); }
438-
catch(e) { Module._wasm_generate(ptr, 0.7, 256); }
439-
Module._free(ptr);
436+
const prompt = getChatPrompt(text);
437+
438+
// Use ccall with async:true — this is the correct way to call
439+
// ASYNCIFY-enabled C functions. Module._fn() direct calls do NOT
440+
// return Promises; only ccall({async:true}) does.
441+
try {
442+
await Module.ccall(
443+
'wasm_generate_async',
444+
'number',
445+
['string', 'number', 'number'],
446+
[prompt, 0.7, 256],
447+
{ async: true }
448+
);
449+
} catch(e) {
450+
console.error('generate error:', e);
451+
}
440452

441453
if (!output && !count) {
442454
aDiv.innerHTML = '<em style="color:#555">No output. Try a different prompt.</em>';

wasm/quant.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wasm/quant.wasm

47 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)