Graceful SIGINT via SharedArrayBuffer with hard-kill fallback for reliable web worker timeout protection
When running user-supplied code in a Pyodide web worker, you need automatic timeout protection. A single strategy isn't enough:\n\n- SharedArrayBuffer SIGINT alone is unreliable: infinite loops in C extensions or tight Python loops may not check the interrupt flag frequently enough.\n- worker.terminate() alone is destructive: it kills the entire Pyodide environment, requiring a full reload (10+ seconds for Pyodide).\n\nThe two-tier approach gives you the best of both:\n\n1. Tier 1 (graceful): After timeout (e.g., 10s), write SIGINT (value 2) to a SharedArrayBuffer. Pyodide checks this buffer periodically and raises KeyboardInterrupt in Python. This preserves the worker and Pyodide state.\n\n2. Tier 2 (hard kill): After a grace period (e.g., 1s), if the call is still pending, call worker.terminate() and respawn. This catches cases where SIGINT couldn't interrupt execution.\n\nKey implementation details:\n- Reset the SAB interrupt flag after the grace period (whether tier 2 fires or not) so it doesn't affect the next call\n- Track pending calls externally (Comlink promises are orphaned on terminate)\n- When cross-origin isolation isn't available (no SharedArrayBuffer), skip straight to tier 2\n- Wrap each RPC call in a Promise with external reject so hard_restart can clean up