@@ -1542,13 +1542,15 @@ class Subtask(Waitable):
15421542 on_cancel: Optional[OnCancel]
15431543 lenders: Optional[list[ResourceHandle]]
15441544 cancellation_requested: bool
1545+ flat_results: list[CoreValType]
15451546
15461547 def __init__ (self ):
15471548 Waitable.__init__ (self )
15481549 self .state = Subtask.State.STARTING
15491550 self .on_cancel = None
15501551 self .lenders = []
15511552 self .cancellation_requested = False
1553+ self .flat_results = []
15521554```
15531555
15541556The ` state ` field of ` Subtask ` tracks the callee's progression from the initial
@@ -1582,6 +1584,13 @@ that were initially incremented.
15821584 lending_handle.num_lends += 1
15831585 self .lenders.append(lending_handle)
15841586
1587+ def resolve (self , state , flat_results ):
1588+ assert (state == Subtask.State.RETURNED or flat_results == [])
1589+ assert (not self .resolved())
1590+ self .state = state
1591+ self .flat_results = flat_results
1592+ assert (self .resolved())
1593+
15851594 def deliver_resolve (self ):
15861595 assert (not self .resolve_delivered() and self .resolved())
15871596 for h in self .lenders:
@@ -3804,23 +3813,19 @@ along with the component instance being instantiated. These are then passed into
38043813` canon_lower ` every time the generated ` CoreFuncInst ` is called, along with the
38053814runtime Core WebAssembly arguments.
38063815
3807- Based on this, ` canon_lower ` is defined in chunks as follows. First, each call
3808- to ` canon_lower ` creates a new ` Subtask ` . However, this ` Subtask ` is only added
3809- to the current component instance's ` handles ` table (below) if ` async ` is
3810- specified * and* ` callee ` blocks. In any case, this ` Subtask ` is used as the
3811- ` LiftLowerContext.borrow_scope ` for ` borrow ` arguments, ensuring that owned
3812- handles are not dropped before ` Subtask.deliver_resolve ` is called (below).
3816+ Based on this, ` canon_lower ` is defined in chunks as follows. First, like most
3817+ Canonical ABI functions callable from Core WebAssembly, lowered imports may not
3818+ be called during ` post-return ` or ` realloc ` :
38133819``` python
38143820def canon_lower (callee , ft , opts , flat_args : list[CoreValType]) -> list[CoreValType]:
38153821 thread = current_thread()
38163822 trap_if(not thread.task.inst.may_leave)
3817- subtask = Subtask()
3818- cx = LiftLowerContext(opts, thread.task.inst, subtask)
38193823```
38203824
3821- The next chunk makes the call to ` callee ` using the ` opts ` immediates of the
3822- ` canon lower ` definition to configure ` lift_flat_values ` and ` lower_flat_values `
3823- (both defined above) and the current instance as the ` caller ` .
3825+ The component-level function type maps to a Core WebAssembly function type using
3826+ different flattening constants based on whether the sync or async ABI is used.
3827+ The values of ` max_flat_{params,results} ` mirror the flattening logic in
3828+ ` flatten_functype ` above:
38243829``` python
38253830 flat_ft = flatten_functype(opts, ft, ' lower' )
38263831 assert (types_match_values(flat_ft.params, flat_args))
@@ -3832,44 +3837,51 @@ The next chunk makes the call to `callee` using the `opts` immediates of the
38323837 else :
38333838 max_flat_params = MAX_FLAT_ASYNC_PARAMS
38343839 max_flat_results = 0
3840+ ```
38353841
3836- on_progress = lambda :()
3837- flat_results = None
3842+ Next, the call is officially started by calling ` callee ` with the required
3843+ ` OnStart ` and ` OnResolve ` callbacks (described above) that lift arguments and
3844+ lower results if not cancelled. The ` maybe_on_progress ` callback starts as a
3845+ no-op but is switched to ` on_progress ` (defined below) in the ` async ` case if
3846+ ` callee ` [ blocks] , so that progress updates are asynchronously delivered to the
3847+ caller.
3848+ ``` python
3849+ subtask = Subtask()
3850+ cx = LiftLowerContext(opts, thread.task.inst, subtask)
3851+ maybe_on_progress = lambda :()
38383852
38393853 def on_start ():
3840- on_progress ()
3854+ maybe_on_progress ()
38413855 assert (subtask.state == Subtask.State.STARTING )
38423856 subtask.state = Subtask.State.STARTED
38433857 return lift_flat_values(cx, max_flat_params, flat_args, ft.param_types())
38443858
38453859 def on_resolve (result ):
3846- on_progress ()
3860+ maybe_on_progress ()
38473861 if result is None :
38483862 assert (subtask.cancellation_requested)
38493863 if subtask.state == Subtask.State.STARTING :
3850- subtask.state = Subtask.State.CANCELLED_BEFORE_STARTED
3864+ subtask.resolve( Subtask.State.CANCELLED_BEFORE_STARTED , [])
38513865 else :
38523866 assert (subtask.state == Subtask.State.STARTED )
3853- subtask.state = Subtask.State.CANCELLED_BEFORE_RETURNED
3867+ subtask.resolve( Subtask.State.CANCELLED_BEFORE_RETURNED , [])
38543868 else :
38553869 assert (subtask.state == Subtask.State.STARTED )
3856- subtask.state = Subtask.State.RETURNED
3857- nonlocal flat_results
38583870 flat_results = lower_flat_values(cx, max_flat_results, result, ft.result_type(), flat_args)
3871+ subtask.resolve(Subtask.State.RETURNED , flat_results)
38593872
38603873 subtask.on_cancel = callee(on_start, on_resolve, caller = thread.task.inst)
38613874 assert (ft.async_ or subtask.state == Subtask.State.RETURNED )
38623875```
3863- The ` Subtask.state ` field is updated by the callbacks to keep track of the
3864- call progress. The ` on_progress ` variable starts as a no-op, but is used by the
3865- ` async ` case below to trigger event delivery.
3876+ According to the ` FuncInst ` calling contract, if ` callee ` [ blocks] , it must
3877+ immediately return an ` OnCancel ` callback which the code above stores in the
3878+ ` Subtask ` to enable subsequent requests for cancellation. As asserted above, if
3879+ the ` callee ` 's function type does not declare the ` async ` effect, ` callee ` must
3880+ not block before returning a value.
38663881
3867- According to the ` FuncInst ` calling contract, the call to ` callee ` should never
3868- "block" (i.e., wait on I/O). If the ` callee ` * would* block, it will instead
3869- return an ` OnCancel ` callback which is stored in the ` Subtask ` (so that it can
3870- be used to request cancellation in the future). Furthermore, if the function
3871- type does not have the ` async ` effect, the function * must* have returned a
3872- value.
3882+ Note that, for component-to-component calls, the ` caller ` of the ` FuncInst ` is
3883+ the current component instance. This information is used by ` may_enter_from ` to
3884+ determine when to trap because ` callee ` is being synchronously reentered.
38733885
38743886In the synchronous case (when the ` async ` ` canonopt ` is not set), if the
38753887` callee ` blocked before calling ` on_resolve ` , the synchronous caller's thread
@@ -3885,9 +3897,9 @@ use a plain synchronous function call instead, as expected.
38853897 if not opts.async_:
38863898 if not subtask.resolved():
38873899 thread.wait_until(subtask.resolved)
3888- assert (types_match_values(flat_ft.results, flat_results))
38893900 subtask.deliver_resolve()
3890- return flat_results
3901+ assert (types_match_values(flat_ft.results, subtask.flat_results))
3902+ return subtask.flat_results
38913903```
38923904The call to ` Subtask.deliver_resolve ` decrements the counters on handles that
38933905were lent for ` borrow ` ed parameters during the call. These counters are
@@ -3906,8 +3918,8 @@ reserved.
39063918``` python
39073919 else :
39083920 if subtask.resolved():
3909- assert (flat_results == [])
39103921 subtask.deliver_resolve()
3922+ assert (subtask.flat_results == [])
39113923 return [Subtask.State.RETURNED ]
39123924 else :
39133925 subtaski = thread.task.inst.handles.add(subtask)
@@ -3917,20 +3929,21 @@ reserved.
39173929 subtask.deliver_resolve()
39183930 return (EventCode.SUBTASK , subtaski, subtask.state)
39193931 subtask.set_pending_event(subtask_event)
3932+ maybe_on_progress = on_progress
39203933 assert (0 < subtaski <= Table.MAX_LENGTH < 2 ** 28 )
39213934 assert (0 <= subtask.state < 2 ** 4 )
39223935 return [subtask.state | (subtaski << 4 )]
39233936```
39243937When ` on_start ` and ` on_resolve ` are called after this initial ` async ` -lowered
3925- call returns, the ` on_progress ` callback (called by ` on_start ` and ` on_resolve ` )
3926- will set a pending event on the ` Subtask ` (which derives ` Waitable ` ) so that it
3927- can be waited on via ` waitable-set.{wait,poll} ` or, if a ` callback ` is used, by
3928- returning to the event loop. If ` on_start ` is called followed by ` on_resolve `
3929- before core wasm receives the first event, core wasm will only receive the
3930- second event, not two events. Note ` Subtask.drop ` prevents (via trap) a
3931- ` Subtask ` from being dropped before ` on_resolve ` is called and the event is
3932- delivered to core wasm to ensure that ` Subtask.deliver_resolve ` always performs
3933- its lend-count accounting.
3938+ call returns, the ` on_progress ` callback (called by ` on_start ` and ` on_resolve `
3939+ above) will set a pending event on the ` Subtask ` (which derives ` Waitable ` ) so
3940+ that it can be waited on via ` waitable-set.{wait,poll} ` or, if a ` callback ` is
3941+ used, by returning to the event loop. If ` on_start ` is called followed by
3942+ ` on_resolve ` before core wasm receives the first event, core wasm will only
3943+ receive the second event, not two events. Note ` Subtask.drop ` prevents (via
3944+ trap) a ` Subtask ` from being dropped before ` on_resolve ` is called and the event
3945+ is delivered to core wasm to ensure that ` Subtask.deliver_resolve ` always
3946+ performs its lend-count accounting.
39343947
39353948
39363949### ` canon resource.new `
0 commit comments