From 7a1af7a80ad531701d994868d0848c0b4fd08dc1 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 8 Apr 2026 21:21:08 +0000 Subject: [PATCH] =?UTF-8?q?WIP:=20bytecode=20when/do/perform=20=E2=80=94?= =?UTF-8?q?=20host-callback=20=5FdriveAsync=20fix=20+=20debugging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause identified: nested cek_call_or_suspend calls on same VM overwrite pending_cek. First call suspends (thunk's hs-wait), second call from synchronous dom-listen callback overwrites before resume. sandbox host-callback: removed _driveAsync call to prevent duplicate resume chains. Still 3/6 in Node.js test — issue is in OCaml call stack nesting, not JS async. Next: prevent pending_cek overwrite in nested CEK→VM→CEK→VM chains. Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/lib/sx_vm.ml | 9 ++++++++- tests/playwright/sx-inspect.js | 7 ++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/hosts/ocaml/lib/sx_vm.ml b/hosts/ocaml/lib/sx_vm.ml index c10e1c14..bda0b056 100644 --- a/hosts/ocaml/lib/sx_vm.ml +++ b/hosts/ocaml/lib/sx_vm.ml @@ -952,7 +952,14 @@ let () = _vm_suspension_to_dict := (fun exn -> Hashtbl.replace d "__vm_suspended" (Bool true); Hashtbl.replace d "request" request; Hashtbl.replace d "resume" (NativeFn ("vm-resume", fun args -> - match args with [result] -> resume_vm vm result | _ -> Nil)); + match args with + | [result] -> + (try resume_vm vm result + with exn2 -> + match !_vm_suspension_to_dict exn2 with + | Some marker -> marker + | None -> raise exn2) + | _ -> Nil)); Some (Dict d) | _ -> None) let () = _cek_eval_lambda_ref := (fun f args -> diff --git a/tests/playwright/sx-inspect.js b/tests/playwright/sx-inspect.js index 2159b291..ea5c0866 100644 --- a/tests/playwright/sx-inspect.js +++ b/tests/playwright/sx-inspect.js @@ -1391,9 +1391,10 @@ async function modeSandbox(page, expr, files, setup, stack, bytecode) { if (typeof fn === 'function' && fn.__sx_handle === undefined) return fn; if (fn && fn.__sx_handle !== undefined) { return function() { - const r = K.callFn(fn, Array.from(arguments)); - window._driveAsync(r); - return r; + // Do NOT call _driveAsync here — the suspension propagates through + // the normal CEK/VM return path. The outer callFn handles it. + // Calling _driveAsync would create a duplicate resume chain. + return K.callFn(fn, Array.from(arguments)); }; } return function() {};