@@ -767,7 +767,7 @@ class WaitableSet:
767767The ` WaitableSet.drop ` method traps if dropped while it still contains elements
768768(whose ` Waitable.wset ` field would become dangling) or if it is being
769769waited-upon by another ` Task ` (as indicated by the ` num_waiting ` field, which
770- is incremented/decremented by ` Task.{wait,poll}_for_event ` below).
770+ is incremented/decremented by ` Task.wait_until ` below).
771771
772772The ` random.shuffle ` in ` get_pending_event ` give embedders the semantic freedom
773773to schedule delivery of events nondeterministically (e.g., taking into account
@@ -1042,31 +1042,10 @@ trap if another task tries to drop the waitable set being used.
10421042 return event
10431043```
10441044
1045- The ` Task.poll_until ` method is called by ` canon_waitable_set_poll ` and from
1046- the event loop in ` canon_lift ` when ` CallbackCode.POLL ` is returned. Unlike
1047- ` wait_until ` , ` poll_until ` does not wait for the given waitable set to have a
1048- pending event, returning ` EventCode.NONE ` if there is none already. However,
1049- ` poll_until ` * does* call ` suspsend_until ` to allow the runtime to
1050- nondeterministically switch to another task (or not).
1051- ``` python
1052- def poll_until (self , ready_func , thread , wset , cancellable ) -> Optional[EventTuple]:
1053- assert (thread in self .threads and thread.task is self )
1054- wset.num_waiting += 1
1055- match self .suspend_until(ready_func, thread, cancellable):
1056- case SuspendResult.CANCELLED :
1057- event = (EventCode.TASK_CANCELLED , 0 , 0 )
1058- case SuspendResult.NOT_CANCELLED :
1059- if wset.has_pending_event():
1060- event = wset.get_pending_event()
1061- else :
1062- event = (EventCode.NONE , 0 , 0 )
1063- wset.num_waiting -= 1
1064- return event
1065- ```
1066-
10671045The ` Task.yield_until ` method is called by ` canon_thread_yield ` and from
1068- the event loop in ` canon_lift ` when ` CallbackCode.YIELD ` is returned.
1069- ` yield_until ` works like ` poll_until ` if given a fresh empty waitable set.
1046+ the event loop in ` canon_lift ` when ` CallbackCode.YIELD ` is returned and
1047+ calls ` suspend_until ` to allow the runtime to nondeterministically switch to
1048+ another task (or not).
10701049``` python
10711050 def yield_until (self , ready_func , thread , cancellable ) -> EventTuple:
10721051 assert (thread in self .threads and thread.task is self )
@@ -3277,11 +3256,8 @@ function (specified as a `funcidx` immediate in `canon lift`) until the
32773256 wset = inst.table.get(si)
32783257 trap_if(not isinstance (wset, WaitableSet))
32793258 event = task.wait_until(lambda : not inst.exclusive, thread, wset, cancellable = True )
3280- case CallbackCode.POLL :
3281- trap_if(not task.may_block())
3282- wset = inst.table.get(si)
3283- trap_if(not isinstance (wset, WaitableSet))
3284- event = task.poll_until(lambda : not inst.exclusive, thread, wset, cancellable = True )
3259+ case _:
3260+ trap()
32853261 thread.in_event_loop = False
32863262 inst.exclusive = True
32873263 event_code, p1, p2 = event
@@ -3290,17 +3266,17 @@ function (specified as a `funcidx` immediate in `canon lift`) until the
32903266 task.exit()
32913267 return
32923268```
3293- The ` Task.{wait,poll, yield}_until ` methods called by the event loop are the
3294- same methods called by the ` yield ` , ` waitable-set.wait ` and ` waitable-set.poll `
3295- built-ins. Thus, the main difference between stackful and stackless async is
3296- whether these suspending operations are performed from an empty or non-empty
3297- core wasm callstack (with the former allowing additional engine optimization).
3269+ The ` Task.{wait,yield}_until ` methods called by the event loop are the same
3270+ methods called by the ` yield ` and ` waitable-set.wait ` built-ins. Thus, the
3271+ main difference between stackful and stackless async is whether these
3272+ suspending operations are performed from an empty or non-empty core wasm
3273+ callstack (with the former allowing additional engine optimization).
32983274
32993275If a ` Task ` is not allowed to block (because it was created for a non-` async ` -
33003276typed function call and has not yet returned a value), ` YIELD ` is always a
3301- no-op and ` WAIT ` and ` POLL ` always trap . Thus, a component may implement a
3277+ no-op and ` WAIT ` always traps . Thus, a component may implement a
33023278non-` async ` -typed function with the ` async callback ` ABI, but the component
3303- * must* call ` task.return ` * before* returning ` WAIT ` or ` POLL ` .
3279+ * must* call ` task.return ` * before* returning ` WAIT ` .
33043280
33053281The event loop also releases ` ComponentInstance.exclusive ` (which was acquired
33063282by ` Task.enter ` and will be released by ` Task.exit ` ) before potentially
@@ -3336,8 +3312,7 @@ class CallbackCode(IntEnum):
33363312 EXIT = 0
33373313 YIELD = 1
33383314 WAIT = 2
3339- POLL = 3
3340- MAX = 3
3315+ MAX = 2
33413316
33423317def unpack_callback_result (packed ):
33433318 code = packed & 0x f
@@ -3347,7 +3322,7 @@ def unpack_callback_result(packed):
33473322 waitable_set_index = packed >> 4
33483323 return (CallbackCode(code), waitable_set_index)
33493324```
3350- The ability to asynchronously wait, poll, yield and exit is thus available to
3325+ The ability to asynchronously wait, yield and exit is thus available to
33513326both the ` callback ` and non-` callback ` cases, making ` callback ` just an
33523327optimization to avoid allocating stacks for async languages that have avoided
33533328the need for stackful coroutines by design (e.g., ` async ` /` await ` in JS,
@@ -3878,26 +3853,22 @@ validation specifies:
38783853* ` $f ` is given type ` (func (param $si i32) (param $ptr i32) (result i32)) `
38793854* 🚟 - ` cancellable ` is allowed (otherwise it must be absent)
38803855
3881- Calling ` $f ` invokes the following function, which returns ` NONE ` ( ` 0 ` ) instead
3882- of blocking if there is no event available, and otherwise returns the event the
3883- same way as ` wait ` .
3856+ Calling ` $f ` invokes the following function, which either returns an event that
3857+ was pending on one of the waitables in the given waitable set ( the same way as
3858+ ` waitable-set.wait ` ) or, if there is none, returns ` 0 ` .
38843859``` python
38853860def canon_waitable_set_poll (cancellable , mem , thread , si , ptr ):
38863861 trap_if(not thread.task.inst.may_leave)
3887- trap_if(not thread.task.may_block())
38883862 wset = thread.task.inst.table.get(si)
38893863 trap_if(not isinstance (wset, WaitableSet))
3890- event = thread.task.poll_until(lambda : True , thread, wset, cancellable)
3864+ if thread.task.deliver_pending_cancel(cancellable):
3865+ event = (EventCode.TASK_CANCELLED , 0 , 0 )
3866+ elif not wset.has_pending_event():
3867+ event = (EventCode.NONE , 0 , 0 )
3868+ else :
3869+ event = wset.get_pending_event()
38913870 return unpack_event(mem, thread, ptr, event)
38923871```
3893- Even though ` waitable-set.poll ` doesn't block until the given waitable set has
3894- a pending event, ` poll_until ` does transitively perform a ` Thread.suspend `
3895- which allows the embedder to nondeterministically switch to executing another
3896- task (like ` thread.yield ` ). To avoid encouraging spin-waiting and to support
3897- hosts like browsers that require returning to the event loop for async I/O to
3898- resolve, a non-` async ` -typed function export that has not yet returned a value
3899- unconditionally traps if it transitively attempts to call ` poll ` .
3900-
39013872If ` cancellable ` is set, then ` waitable-set.poll ` will return whether the
39023873supertask has already or concurrently requested cancellation.
39033874` waitable-set.poll ` (and other cancellable operations) will only indicate
@@ -3927,8 +3898,8 @@ def canon_waitable_set_drop(thread, i):
39273898 return []
39283899```
39293900Note that ` WaitableSet.drop ` will trap if it is non-empty or there is a
3930- concurrent ` waitable-set.wait ` or ` waitable-set.poll ` or ` async callback `
3931- currently using this waitable set.
3901+ concurrent ` waitable-set.wait ` or ` async callback ` currently using this
3902+ waitable set.
39323903
39333904
39343905### 🔀 ` canon waitable.join `
@@ -4607,7 +4578,7 @@ def canon_thread_yield(cancellable, thread):
46074578If a non-` async ` -typed function export that has not yet returned a value
46084579transitively calls ` thread.yield ` , it returns immediately without blocking
46094580(instead of trapping, as with other possibly-blocking operations like
4610- ` waitable-set.poll ` ). This is because, unlike other built-ins, ` thread.yield `
4581+ ` waitable-set.wait ` ). This is because, unlike other built-ins, ` thread.yield `
46114582may be scattered liberally throughout code that might show up in the transitive
46124583call tree of a synchronous function call.
46134584
0 commit comments