Skip to content

Commit db8d72e

Browse files
committed
Switch from implicit to explicit drop of async subtasks
1 parent 41bef2a commit db8d72e

File tree

6 files changed

+55
-8
lines changed

6 files changed

+55
-8
lines changed

Diff for: design/mvp/Async.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,8 @@ features will be added in future chunks to complete "async" in Preview 3:
451451
concurrency
452452
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
453453
no longer wanted and to please wrap it up promptly
454-
* `subtask.drop`: allow tail-calling a subtask so that the current wasm
455-
instance can be torn down eagerly
454+
* allow "tail-calling" a subtask so that the current wasm instance can be torn
455+
down eagerly
456456
* `task.index`+`task.wake`: allow tasks in the same instance to wait on and
457457
wake each other (async condvar-style)
458458
* `nonblocking` function type attribute: allow a function to declare in its

Diff for: design/mvp/Binary.md

+1
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
282282
| 0x0b => (canon task.wait (core func))
283283
| 0x0c => (canon task.poll (core func))
284284
| 0x0d => (canon task.yield (core func))
285+
| 0x0e => (canon subtask.drop (core func))
285286
opts ::= opt*:vec(<canonopt>) => opt*
286287
canonopt ::= 0x00 => string-encoding=utf8
287288
| 0x01 => string-encoding=utf16

Diff for: design/mvp/CanonicalABI.md

+23-3
Original file line numberDiff line numberDiff line change
@@ -619,9 +619,6 @@ once).
619619
def process_event(self, subtask):
620620
assert(subtask.supertask is self)
621621
subtask.enqueued = False
622-
if subtask.state == AsyncCallState.DONE:
623-
self.inst.async_subtasks.remove(subtask.index)
624-
self.num_async_subtasks -= 1
625622
return (EventCode(subtask.state), subtask.index)
626623
```
627624
The `pending_tasks` queue (appended to by `enter` above) is emptied by `wait`
@@ -2399,6 +2396,29 @@ async def canon_task_yield(task):
23992396
return []
24002397
```
24012398

2399+
### 🔀 `canon subtask.drop`
2400+
2401+
For a canonical definition:
2402+
```wasm
2403+
(canon subtask.drop (core func $f))
2404+
```
2405+
validation specifies:
2406+
* `$f` is given type `(func (param i32))`
2407+
2408+
Calling `$f` removes the indicated subtask from the instance's table, trapping
2409+
if the subtask isn't done or isn't a subtask of the current task. The guard
2410+
on `enqueued` ensures that supertasks can only drop subtasks once they've been
2411+
officially notified of their completion (via `task.wait` or callback).
2412+
```python
2413+
async def canon_subtask_drop(task, i):
2414+
subtask = task.inst.async_subtasks.remove(i)
2415+
trap_if(subtask.enqueued)
2416+
trap_if(subtask.state != AsyncCallState.DONE)
2417+
trap_if(subtask.supertask is not task)
2418+
task.num_async_subtasks -= 1
2419+
return []
2420+
```
2421+
24022422
### 🧵 `canon thread.spawn`
24032423

24042424
For a canonical definition:

Diff for: design/mvp/Explainer.md

+5
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,7 @@ canon ::= ...
13111311
| (canon task.wait (core func <id>?)) 🔀
13121312
| (canon task.poll (core func <id>?)) 🔀
13131313
| (canon task.yield (core func <id>?)) 🔀
1314+
| (canon subtask.drop (core func <id>?)) 🔀
13141315
| (canon thread.spawn <typeidx> (core func <id>?)) 🧵
13151316
| (canon thread.hw_concurrency (core func <id>?)) 🧵
13161317
```
@@ -1409,6 +1410,10 @@ switch to another task, allowing a long-running computation to cooperatively
14091410
interleave with other tasks. (See also [`canon_task_yield`] in the Canonical
14101411
ABI explainer.)
14111412

1413+
The `subtask.drop` built-in has type `[i32] -> []` and removes the indicated
1414+
[subtask](Async.md#subtask-and-supertask) from the current instance's subtask
1415+
table, trapping if the subtask isn't done.
1416+
14121417
##### 🧵 Threading built-ins
14131418

14141419
The [shared-everything-threads] proposal adds component model built-ins for

Diff for: design/mvp/canonical-abi/definitions.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,6 @@ def maybe_start_pending_task(self):
461461
def process_event(self, subtask):
462462
assert(subtask.supertask is self)
463463
subtask.enqueued = False
464-
if subtask.state == AsyncCallState.DONE:
465-
self.inst.async_subtasks.remove(subtask.index)
466-
self.num_async_subtasks -= 1
467464
return (EventCode(subtask.state), subtask.index)
468465

469466
def poll(self):
@@ -1523,3 +1520,13 @@ async def canon_task_yield(task):
15231520
trap_if(task.opts.callback is not None)
15241521
await task.yield_()
15251522
return []
1523+
1524+
### 🔀 `canon subtask.drop`
1525+
1526+
async def canon_subtask_drop(task, i):
1527+
subtask = task.inst.async_subtasks.remove(i)
1528+
trap_if(subtask.enqueued)
1529+
trap_if(subtask.state != AsyncCallState.DONE)
1530+
trap_if(subtask.supertask is not task)
1531+
task.num_async_subtasks -= 1
1532+
return []

Diff for: design/mvp/canonical-abi/run_tests.py

+14
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ async def consumer(task, args):
549549
event, callidx = await task.wait()
550550
assert(event == EventCode.CALL_DONE)
551551
assert(callidx == 1)
552+
assert(task.num_async_subtasks == 1)
553+
await canon_subtask_drop(task, callidx)
552554
assert(task.num_async_subtasks == 0)
553555

554556
dtor_fut = asyncio.Future()
@@ -571,6 +573,7 @@ async def dtor(task, args):
571573
event, callidx = await task.wait()
572574
assert(event == AsyncCallState.DONE)
573575
assert(callidx == 1)
576+
await canon_subtask_drop(task, callidx)
574577
assert(task.num_async_subtasks == 0)
575578

576579
[] = await canon_task_return(task, CoreFuncType(['i32'],[]), [42])
@@ -632,6 +635,7 @@ async def callback(task, args):
632635
if args[0] == 42:
633636
assert(args[1] == EventCode.CALL_DONE)
634637
assert(args[2] == 1)
638+
await canon_subtask_drop(task, 1)
635639
return [53]
636640
elif args[0] == 52:
637641
assert(args[1] == EventCode.YIELDED)
@@ -642,6 +646,7 @@ async def callback(task, args):
642646
assert(args[0] == 62)
643647
assert(args[1] == EventCode.CALL_DONE)
644648
assert(args[2] == 2)
649+
await canon_subtask_drop(task, 2)
645650
[] = await canon_task_return(task, CoreFuncType(['i32'],[]), [83])
646651
return [0]
647652

@@ -708,6 +713,7 @@ async def consumer(task, args):
708713
event, callidx = await task.wait()
709714
assert(event == EventCode.CALL_DONE)
710715
assert(callidx == 1)
716+
await canon_subtask_drop(task, callidx)
711717
assert(producer1_done == True)
712718

713719
assert(producer2_done == False)
@@ -716,6 +722,7 @@ async def consumer(task, args):
716722
event, callidx = task.poll()
717723
assert(event == EventCode.CALL_DONE)
718724
assert(callidx == 2)
725+
await canon_subtask_drop(task, callidx)
719726
assert(producer2_done == True)
720727

721728
assert(task.poll() is None)
@@ -796,6 +803,9 @@ async def consumer(task, args):
796803
assert(callidx == 2)
797804
assert(producer2_done == True)
798805

806+
await canon_subtask_drop(task, 1)
807+
await canon_subtask_drop(task, 2)
808+
799809
assert(task.poll() is None)
800810

801811
await canon_task_start(task, CoreFuncType([],[]), [])
@@ -851,6 +861,10 @@ async def core_func(task, args):
851861
event, callidx = await task.wait()
852862
assert(event == EventCode.CALL_DONE)
853863
assert(callidx == 2)
864+
865+
await canon_subtask_drop(task, 1)
866+
await canon_subtask_drop(task, 2)
867+
854868
return []
855869

856870
inst = ComponentInstance()

0 commit comments

Comments
 (0)