fix record io bug

This commit is contained in:
Dmitry Vasilev
2023-07-15 17:59:58 +03:00
parent e8f41d9659
commit 4118bc63d2
3 changed files with 63 additions and 22 deletions

View File

@@ -154,8 +154,10 @@ const make_patched_method = (original, name, use_context) => {
) )
){ ){
cxt.io_trace_is_replay_aborted = true cxt.io_trace_is_replay_aborted = true
// Try to finish fast cxt.io_trace_abort_replay()
// TODO invoke callback to notify that code must be restarted? // throw error to prevent further code execution. It
// is not necesseary, becuase execution would not have
// any effects anyway
const error = new Error('io replay aborted') const error = new Error('io replay aborted')
error.__ignore = true error.__ignore = true
throw error throw error
@@ -188,7 +190,7 @@ const make_patched_method = (original, name, use_context) => {
const next_event = cxt.io_trace[cxt.io_trace_index] const next_event = cxt.io_trace[cxt.io_trace_index]
if(next_event.type == 'call') { if(next_event.type == 'call') {
cxt.io_trace_is_replay_aborted = true cxt.io_trace_is_replay_aborted = true
// TODO reject? Test for never resolved cxt.io_trace_abort_replay()
} else { } else {
while( while(
cxt.io_trace_index < cxt.io_trace.length cxt.io_trace_index < cxt.io_trace.length

View File

@@ -29,9 +29,18 @@ const gen_to_promise = gen_fn => {
} }
} }
const make_promise_with_rejector = cxt => {
let rejector
const p = new cxt.window.Promise(r => rejector = r)
return [p, rejector]
}
const do_run = function*(module_fns, cxt, io_trace){ const do_run = function*(module_fns, cxt, io_trace){
let calltree let calltree
const [replay_aborted_promise, io_trace_abort_replay] =
make_promise_with_rejector(cxt)
cxt = (io_trace == null || io_trace.length == 0) cxt = (io_trace == null || io_trace.length == 0)
// TODO group all io_trace_ properties to single object? // TODO group all io_trace_ properties to single object?
? {...cxt, ? {...cxt,
@@ -46,32 +55,38 @@ const do_run = function*(module_fns, cxt, io_trace){
// Map of (index in io_trace) -> resolve // Map of (index in io_trace) -> resolve
io_trace_resolvers: new Map(), io_trace_resolvers: new Map(),
io_trace_index: 0, io_trace_index: 0,
io_trace_abort_replay,
} }
apply_promise_patch(cxt) apply_promise_patch(cxt)
set_current_context(cxt) set_current_context(cxt)
for(let {module, fn} of module_fns) { for(let {module, fn} of module_fns) {
cxt.found_call = null cxt.found_call = null
cxt.children = null cxt.children = null
calltree = { calltree = {
toplevel: true, toplevel: true,
module, module,
id: cxt.call_counter++ id: cxt.call_counter++
} }
try { try {
cxt.modules[module] = {} cxt.modules[module] = {}
yield fn(cxt, __trace, __trace_call, __do_await) const result = fn(cxt, __trace, __trace_call, __do_await)
calltree.ok = true if(result instanceof cxt.window.Promise) {
} catch(error) { yield cxt.window.Promise.race([replay_aborted_promise, result])
calltree.ok = false } else {
calltree.error = error yield result
} }
calltree.children = cxt.children calltree.ok = true
if(!calltree.ok) { } catch(error) {
break calltree.ok = false
} calltree.error = error
}
calltree.children = cxt.children
if(!calltree.ok) {
break
}
} }
cxt.is_recording_deferred_calls = true cxt.is_recording_deferred_calls = true

View File

@@ -3416,4 +3416,28 @@ const y = x()`
'object', 'object',
) )
}), }),
test('record io hangs bug', async () => {
patch_builtin(
'fetch',
() => new Promise(resolve => original_setTimeout(resolve, 0))
)
const code = `
const p = fetch('')
Math.random()
await p
`
const i = await test_initial_state_async(code)
assert_equal(i.io_trace.length, 3)
const next_code = `await fetch('')`
const state = await command_input_async(i, next_code, 0)
assert_equal(state.io_trace.length, 2)
patch_builtin('fetch', null)
}),
] ]