fix(wasm): Fix Asyncify modal errors with global lock mechanism
Update wxwidgets submodule with fix for consecutive modal dialog crashes. The fix prevents overlapping Asyncify operations that caused "indirect call to null" and "func is not a function" errors when a second modal was triggered immediately after the first one completed. Also includes: - docs: Clarify build script order and descriptions in CLAUDE.md - refactor(test): Remove debug logging from wizard test Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
528ed97692
commit
51b158f6b1
4 changed files with 45 additions and 13 deletions
|
|
@ -12,8 +12,8 @@ The tests have log files in tests/logs/{wxwidgets/kicad}/{test-name} after each
|
|||
Always check screenshots for validating tests
|
||||
Run e2e tests from /tests folder: `npm run test:kicad` or `npm run test:e2e` (not playwright directly)
|
||||
|
||||
Build wxwidgets with scripts/build-wxuniversal-wasm.sh
|
||||
Build kicad with docker/build.sh
|
||||
Build kicad with docker/build.sh (includes wxwidgets build, runs in docker)
|
||||
Build wxwidgets standalone with scripts/build-wxuniversal-wasm.sh (runs on machine, for wxwidgets-only changes)
|
||||
Build CPP wxwidgets tests with scripts/builds-wasm-test.sh
|
||||
The build scripts pipe their outputs into log files so that they won't clog the LLM context.
|
||||
Don't pipe outputs, just run the scripts. Maybe with flex if you need that.
|
||||
|
|
|
|||
40
learning.md
Normal file
40
learning.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Learning Notes
|
||||
|
||||
## Asyncify and Consecutive Modal Dialogs
|
||||
|
||||
### Problem
|
||||
When multiple modal dialogs are triggered in quick succession, Asyncify operations can overlap causing crashes:
|
||||
- "indirect call to null"
|
||||
- "func is not a function"
|
||||
- "index out of bounds"
|
||||
|
||||
### Root Cause
|
||||
Per Emscripten docs: "It is not safe to start an async operation while another is already running."
|
||||
|
||||
When the first modal completes:
|
||||
1. Asyncify begins rewinding the C++ stack
|
||||
2. C++ code triggers second modal before rewind completes
|
||||
3. Second modal's Asyncify operation conflicts with first modal's cleanup
|
||||
4. Asyncify state corruption occurs
|
||||
|
||||
### Key Insight
|
||||
**You cannot use ANY Asyncify mechanism to wait** - neither `EM_ASYNC_JS` await nor `emscripten_sleep()` - while another Asyncify operation is cleaning up. Both use Asyncify internally and cause the same conflict.
|
||||
|
||||
### Solution Pattern
|
||||
1. Use a **global lock** to track when Asyncify is busy
|
||||
2. Check lock with **synchronous JS** (`EM_JS`, not `EM_ASYNC_JS`) - this doesn't use Asyncify
|
||||
3. If locked, **return immediately** instead of waiting
|
||||
4. Release lock via **double setTimeout(0)** to ensure Asyncify fully completes before allowing new operations
|
||||
|
||||
```javascript
|
||||
// Good: Synchronous check (no Asyncify)
|
||||
EM_JS(int, isLocked, (), { return Module._locked ? 1 : 0; });
|
||||
|
||||
// Bad: This uses Asyncify and will cause conflicts
|
||||
while (isLocked()) {
|
||||
emscripten_sleep(10); // Uses Asyncify!
|
||||
}
|
||||
```
|
||||
|
||||
### File Reference
|
||||
`wxwidgets/src/wasm/dialog.cpp` - Modal implementation with lock mechanism
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { test, expect } from './fixtures';
|
||||
import { clickByLabel, dumpElements } from '../e2e/utils/element-tracker';
|
||||
import { clickByLabel } from '../e2e/utils/element-tracker';
|
||||
|
||||
/**
|
||||
* PCBnew WASM E2E Tests
|
||||
|
|
@ -20,24 +20,17 @@ test.describe('PCBnew WASM', () => {
|
|||
|
||||
// Click through wizard - try Next >, then Finish if not found
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
// Dump buttons before clicking to see what's available
|
||||
console.log(`Step ${i}: Looking for buttons...`);
|
||||
await dumpElements(page);
|
||||
|
||||
// Try "Next >" first (exact button label)
|
||||
let clicked = await clickByLabel(page, 'Next >');
|
||||
if (clicked) {
|
||||
console.log(`Step ${i}: Clicked "Next >"`);
|
||||
// Continue to next page
|
||||
} else {
|
||||
// Try Finish button
|
||||
clicked = await clickByLabel(page, 'Finish');
|
||||
if (clicked) {
|
||||
console.log(`Step ${i}: Clicked "Finish"`);
|
||||
await page.waitForTimeout(500);
|
||||
await page.screenshot({ path: `test-results/wizard-${String(i).padStart(2, '0')}-finish.png` });
|
||||
break;
|
||||
} else {
|
||||
console.log(`Step ${i}: No "Next >" or "Finish" found`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +44,6 @@ test.describe('PCBnew WASM', () => {
|
|||
|
||||
// Verify PCBnew loaded
|
||||
const canvasCount = await page.locator('canvas').count();
|
||||
console.log('Canvas count after wizard:', canvasCount);
|
||||
expect(canvasCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 47ebb3991d91614ca7cd6396bb6305a73d184232
|
||||
Subproject commit dd370ac191d5f20ea847b1bce4a611464f5baa65
|
||||
Loading…
Reference in a new issue