From 6d96fc3659398bb93a4cf0a1b3e480af78febb60 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 16 May 2025 11:21:31 -0500 Subject: [PATCH] CABI: Remove the zero-index special case for callback return value --- design/mvp/Async.md | 6 +++++- design/mvp/CanonicalABI.md | 14 ++++---------- design/mvp/canonical-abi/definitions.py | 10 ++++------ design/mvp/canonical-abi/run_tests.py | 5 +++-- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/design/mvp/Async.md b/design/mvp/Async.md index 6ee00893..e924a217 100644 --- a/design/mvp/Async.md +++ b/design/mvp/Async.md @@ -906,7 +906,11 @@ not externally-visible behavior. (func (export "cb") (param $event i32) (param $p1 i32) (param $p2 i32) ... if (result i32) ;; if subtasks remain: - i32.const 2 ;; return WAIT + (i32.or ;; return (WAIT | ($wsi << 4)) + (i32.const 2) + (i32.shl + (global.get $wsi) + (i32.const 4))) else ;; if no subtasks remain: ... call $task_return ;; return the string result (pointer,length) diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md index c8e0b2aa..9d121337 100644 --- a/design/mvp/CanonicalABI.md +++ b/design/mvp/CanonicalABI.md @@ -3004,12 +3004,8 @@ returning to an event loop in `canon_lift`, with the `callback` called repeatedly until the `EXIT` code is returned: ```python [packed] = await call_and_trap_on_throw(callee, task, flat_args) - s = None while True: code,si = unpack_callback_result(packed) - if si != 0: - s = task.inst.table.get(si) - trap_if(not isinstance(s, WaitableSet)) match code: case CallbackCode.EXIT: task.exit() @@ -3017,18 +3013,16 @@ repeatedly until the `EXIT` code is returned: case CallbackCode.YIELD: e = await task.yield_(sync = False) case CallbackCode.WAIT: - trap_if(not s) + s = task.inst.table.get(si) + trap_if(not isinstance(s, WaitableSet)) e = await task.wait_for_event(s, sync = False) case CallbackCode.POLL: - trap_if(not s) + s = task.inst.table.get(si) + trap_if(not isinstance(s, WaitableSet)) e = await task.poll_for_event(s, sync = False) event_code, p1, p2 = e [packed] = await call_and_trap_on_throw(opts.callback, task, [event_code, p1, p2]) ``` -One detail worth noting here is that the index of the waitable set does not need -to be returned every time; as an optimization to avoid a table access on every -turn of the event loop, if the returned waitable set index is `0` (which is an -invalid table index), the previous waitable set will be used. The bit-packing scheme used for the `i32` `packed` return value is defined as follows: diff --git a/design/mvp/canonical-abi/definitions.py b/design/mvp/canonical-abi/definitions.py index 5466b6dd..a1446971 100644 --- a/design/mvp/canonical-abi/definitions.py +++ b/design/mvp/canonical-abi/definitions.py @@ -1866,12 +1866,8 @@ async def canon_lift(opts, inst, ft, callee, caller, on_start, on_resolve, on_bl return [packed] = await call_and_trap_on_throw(callee, task, flat_args) - s = None while True: code,si = unpack_callback_result(packed) - if si != 0: - s = task.inst.table.get(si) - trap_if(not isinstance(s, WaitableSet)) match code: case CallbackCode.EXIT: task.exit() @@ -1879,10 +1875,12 @@ async def canon_lift(opts, inst, ft, callee, caller, on_start, on_resolve, on_bl case CallbackCode.YIELD: e = await task.yield_(sync = False) case CallbackCode.WAIT: - trap_if(not s) + s = task.inst.table.get(si) + trap_if(not isinstance(s, WaitableSet)) e = await task.wait_for_event(s, sync = False) case CallbackCode.POLL: - trap_if(not s) + s = task.inst.table.get(si) + trap_if(not isinstance(s, WaitableSet)) e = await task.poll_for_event(s, sync = False) event_code, p1, p2 = e [packed] = await call_and_trap_on_throw(opts.callback, task, [event_code, p1, p2]) diff --git a/design/mvp/canonical-abi/run_tests.py b/design/mvp/canonical-abi/run_tests.py index 5b183041..01058470 100644 --- a/design/mvp/canonical-abi/run_tests.py +++ b/design/mvp/canonical-abi/run_tests.py @@ -683,6 +683,7 @@ async def core_producer_pre(fut, task, args): producer2 = partial(canon_lift, producer_opts, producer_inst, producer_ft, core_producer2) consumer_ft = FuncType([],[U32Type()]) + seti = 0 async def consumer(task, args): assert(len(args) == 0) @@ -696,6 +697,7 @@ async def consumer(task, args): assert(subi2 == 2) assert(state == Subtask.State.STARTED) + nonlocal seti [seti] = await canon_waitable_set_new(task) assert(seti == 3) [] = await canon_waitable_join(task, subi1, seti) @@ -707,7 +709,6 @@ async def consumer(task, args): async def callback(task, args): assert(len(args) == 3) - seti = 1 [ctx] = await canon_context_get('i32', 0, task) match ctx: case 42: @@ -723,7 +724,7 @@ async def callback(task, args): assert(args[2] == 0) fut2.set_result(None) [] = await canon_context_set('i32', 0, task, 62) - return [definitions.CallbackCode.WAIT] + return [definitions.CallbackCode.WAIT | (seti << 4)] case 62: assert(args[0] == EventCode.SUBTASK) assert(args[1] == 2)