Skip to content

Commit 94c968c

Browse files
committed
Make 'async' part of the function type, not a hint
1 parent a0c6dac commit 94c968c

File tree

7 files changed

+567
-339
lines changed

7 files changed

+567
-339
lines changed

design/mvp/Binary.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ valtype ::= i:<typeidx> => i
216216
resourcetype ::= 0x3f 0x7f f?:<funcidx>? => (resource (rep i32) (dtor f)?)
217217
| 0x3e 0x7f f:<funcidx> cb?:<funcidx>? => (resource (rep i32) (dtor async f (callback cb)?)) 🚝
218218
functype ::= 0x40 ps:<paramlist> rs:<resultlist> => (func ps rs)
219+
| 0x43 ps:<paramlist> rs:<resultlist> => (func async ps rs)
219220
paramlist ::= lt*:vec(<labelvaltype>) => (param lt)*
220221
resultlist ::= 0x00 t:<valtype> => (result t)
221222
| 0x01 0x00 =>
@@ -288,7 +289,6 @@ canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
288289
| 0x01 0x00 f:<funcidx> opts:<opts> => (canon lower f opts (core func))
289290
| 0x02 rt:<typeidx> => (canon resource.new rt (core func))
290291
| 0x03 rt:<typeidx> => (canon resource.drop rt (core func))
291-
| 0x07 rt:<typeidx> => (canon resource.drop rt async (core func)) 🚝
292292
| 0x04 rt:<typeidx> => (canon resource.rep rt (core func))
293293
| 0x08 => (canon backpressure.set (core func)) 🔀✕
294294
| 0x24 => (canon backpressure.inc (core func)) 🔀
@@ -515,7 +515,8 @@ named once.
515515

516516
* The opcodes (for types, canon built-ins, etc) should be re-sorted
517517
* The two `depname` cases should be merged into one (`dep=<...>`)
518-
* The two `list` type codes should be merged into one with an optional immediate.
518+
* The two `list` type codes should be merged into one with an optional immediate
519+
and similarly for `func`.
519520
* The `0x00` variant of `importname'` and `exportname'` will be removed. Any
520521
remaining variant(s) will be renumbered or the prefix byte will be removed or
521522
repurposed.

design/mvp/CanonicalABI.md

Lines changed: 121 additions & 37 deletions
Large diffs are not rendered by default.

design/mvp/Concurrency.md

Lines changed: 242 additions & 153 deletions
Large diffs are not rendered by default.

design/mvp/Explainer.md

Lines changed: 68 additions & 68 deletions
Large diffs are not rendered by default.

design/mvp/WIT.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,16 +1450,11 @@ named-type-list ::= ϵ
14501450
named-type ::= id ':' ty
14511451
```
14521452

1453-
The optional `async` hint in a WIT function type indicates that the callee
1454-
is expected to block and thus the caller should emit whatever asynchronous
1455-
language bindings are appropriate (e.g., in JS, Python, C# or Rust, an `async`
1456-
WIT function would emit an `async` JS/Python/C#/Rust function). Because `async`
1457-
is just a hint and not enforced by the runtime, it is technically possible for
1458-
a non-`async` callee to block. In that case, though, it is the *callee's* fault
1459-
for any resultant loss of concurrency, not the caller's. Thus, `async` is
1460-
primarily intended to document expectations in a way that can be taken
1461-
advantage of by bindings generators. (For more details, see the [concurrency
1462-
explainer](Concurrency.md).)
1453+
The optional `async` prefix in a WIT function type indicates that the callee
1454+
may block and thus the caller should use the async ABI and asynchronous
1455+
source-language bindings (e.g., `async` functions in JS, Python, C# or Rust) if
1456+
concurrency execution is desired. For more details, see the [concurrency
1457+
explainer](Concurrency.md#summary).
14631458

14641459

14651460
## Item: `use`
@@ -1689,8 +1684,8 @@ resource-method ::= func-item
16891684
| 'constructor' param-list ';'
16901685
```
16911686

1692-
The optional `async` hint on `static` functions has the same meaning as
1693-
in a non-`static` `func-item`.
1687+
The optional `async` on `static` functions has the same meaning as in a
1688+
non-`static` `func-item`.
16941689

16951690
The syntax for handle types is presented [below](#handles).
16961691

design/mvp/canonical-abi/definitions.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class InstanceType(ExternType):
8888
class FuncType(ExternType):
8989
params: list[tuple[str,ValType]]
9090
result: list[ValType|tuple[str,ValType]]
91+
async_: bool = False
9192
def param_types(self):
9293
return self.extract_types(self.params)
9394
def result_type(self):
@@ -402,6 +403,7 @@ def resume(self, suspend_result = SuspendResult.NOT_CANCELLED):
402403
assert(not self.running())
403404

404405
def suspend(self, cancellable) -> SuspendResult:
406+
assert(self.task.may_block())
405407
assert(self.running() and not self.cancellable and self.suspend_result is None)
406408
self.cancellable = cancellable
407409
self.parent_lock.release()
@@ -420,6 +422,7 @@ def resume_later(self):
420422
self.task.inst.store.pending.append(self)
421423

422424
def suspend_until(self, ready_func, cancellable = False) -> SuspendResult:
425+
assert(self.task.may_block())
423426
assert(self.running())
424427
if ready_func() and not DETERMINISTIC_PROFILE and random.randint(0,1):
425428
return SuspendResult.NOT_CANCELLED
@@ -566,8 +569,13 @@ def trap_if_on_the_stack(self, inst):
566569
def needs_exclusive(self):
567570
return not self.opts.async_ or self.opts.callback
568571

572+
def may_block(self):
573+
return self.ft.async_ or self.state == Task.State.RESOLVED
574+
569575
def enter(self, thread):
570576
assert(thread in self.threads and thread.task is self)
577+
if not self.ft.async_:
578+
return True
571579
def has_backpressure():
572580
return self.inst.backpressure > 0 or (self.needs_exclusive() and self.inst.exclusive)
573581
if has_backpressure() or self.inst.num_waiting_to_enter > 0:
@@ -584,6 +592,8 @@ def has_backpressure():
584592

585593
def exit(self):
586594
assert(len(self.threads) > 0)
595+
if not self.ft.async_:
596+
return
587597
if self.needs_exclusive():
588598
assert(self.inst.exclusive)
589599
self.inst.exclusive = False
@@ -2023,12 +2033,17 @@ def thread_func(thread):
20232033
inst.exclusive = False
20242034
match code:
20252035
case CallbackCode.YIELD:
2026-
event = task.yield_until(lambda: not inst.exclusive, thread, cancellable = True)
2036+
if task.may_block():
2037+
event = task.yield_until(lambda: not inst.exclusive, thread, cancellable = True)
2038+
else:
2039+
event = (EventCode.NONE, 0, 0)
20272040
case CallbackCode.WAIT:
2041+
trap_if(not task.may_block())
20282042
wset = inst.table.get(si)
20292043
trap_if(not isinstance(wset, WaitableSet))
20302044
event = task.wait_until(lambda: not inst.exclusive, thread, wset, cancellable = True)
20312045
case CallbackCode.POLL:
2046+
trap_if(not task.may_block())
20322047
wset = inst.table.get(si)
20332048
trap_if(not isinstance(wset, WaitableSet))
20342049
event = task.poll_until(lambda: not inst.exclusive, thread, wset, cancellable = True)
@@ -2069,6 +2084,8 @@ def call_and_trap_on_throw(callee, thread, args):
20692084

20702085
def canon_lower(opts, ft, callee: FuncInst, thread, flat_args):
20712086
trap_if(not thread.task.inst.may_leave)
2087+
trap_if(not thread.task.may_block() and ft.async_ and not opts.async_)
2088+
20722089
subtask = Subtask()
20732090
cx = LiftLowerContext(opts, thread.task.inst, subtask)
20742091

@@ -2108,6 +2125,7 @@ def on_resolve(result):
21082125
flat_results = lower_flat_values(cx, max_flat_results, result, ft.result_type(), flat_args)
21092126

21102127
subtask.callee = callee(thread.task, on_start, on_resolve)
2128+
assert(ft.async_ or subtask.state == Subtask.State.RETURNED)
21112129

21122130
if not opts.async_:
21132131
if not subtask.resolved():
@@ -2142,31 +2160,30 @@ def canon_resource_new(rt, thread, rep):
21422160

21432161
### `canon resource.drop`
21442162

2145-
def canon_resource_drop(rt, async_, thread, i):
2163+
def canon_resource_drop(rt, thread, i):
21462164
trap_if(not thread.task.inst.may_leave)
21472165
inst = thread.task.inst
21482166
h = inst.table.remove(i)
21492167
trap_if(not isinstance(h, ResourceHandle))
21502168
trap_if(h.rt is not rt)
21512169
trap_if(h.num_lends != 0)
2152-
flat_results = [] if not async_ else [0]
21532170
if h.own:
21542171
assert(h.borrow_scope is None)
21552172
if inst is rt.impl:
21562173
if rt.dtor:
21572174
rt.dtor(h.rep)
21582175
else:
21592176
if rt.dtor:
2160-
caller_opts = CanonicalOptions(async_ = async_)
2177+
caller_opts = CanonicalOptions(async_ = False)
21612178
callee_opts = CanonicalOptions(async_ = rt.dtor_async, callback = rt.dtor_callback)
2162-
ft = FuncType([U32Type()],[])
2179+
ft = FuncType([U32Type()],[], async_ = False)
21632180
callee = partial(canon_lift, callee_opts, rt.impl, ft, rt.dtor)
2164-
flat_results = canon_lower(caller_opts, ft, callee, thread, [h.rep])
2181+
[] = canon_lower(caller_opts, ft, callee, thread, [h.rep])
21652182
else:
21662183
thread.task.trap_if_on_the_stack(rt.impl)
21672184
else:
21682185
h.borrow_scope.num_borrows -= 1
2169-
return flat_results
2186+
return []
21702187

21712188
### `canon resource.rep`
21722189

@@ -2244,6 +2261,7 @@ def canon_waitable_set_new(thread):
22442261

22452262
def canon_waitable_set_wait(cancellable, mem, thread, si, ptr):
22462263
trap_if(not thread.task.inst.may_leave)
2264+
trap_if(not thread.task.may_block())
22472265
wset = thread.task.inst.table.get(si)
22482266
trap_if(not isinstance(wset, WaitableSet))
22492267
event = thread.task.wait_until(lambda: True, thread, wset, cancellable)
@@ -2260,6 +2278,7 @@ def unpack_event(mem, thread, ptr, e: EventTuple):
22602278

22612279
def canon_waitable_set_poll(cancellable, mem, thread, si, ptr):
22622280
trap_if(not thread.task.inst.may_leave)
2281+
trap_if(not thread.task.may_block())
22632282
wset = thread.task.inst.table.get(si)
22642283
trap_if(not isinstance(wset, WaitableSet))
22652284
event = thread.task.poll_until(lambda: True, thread, wset, cancellable)
@@ -2294,6 +2313,7 @@ def canon_waitable_join(thread, wi, si):
22942313

22952314
def canon_subtask_cancel(async_, thread, i):
22962315
trap_if(not thread.task.inst.may_leave)
2316+
trap_if(not thread.task.may_block() and not async_)
22972317
subtask = thread.task.inst.table.get(i)
22982318
trap_if(not isinstance(subtask, Subtask))
22992319
trap_if(subtask.resolve_delivered())
@@ -2350,6 +2370,8 @@ def canon_stream_write(stream_t, opts, thread, i, ptr, n):
23502370

23512371
def stream_copy(EndT, BufferT, event_code, stream_t, opts, thread, i, ptr, n):
23522372
trap_if(not thread.task.inst.may_leave)
2373+
trap_if(not thread.task.may_block() and not opts.async_)
2374+
23532375
e = thread.task.inst.table.get(i)
23542376
trap_if(not isinstance(e, EndT))
23552377
trap_if(e.shared.t != stream_t.t)
@@ -2401,6 +2423,8 @@ def canon_future_write(future_t, opts, thread, i, ptr):
24012423

24022424
def future_copy(EndT, BufferT, event_code, future_t, opts, thread, i, ptr):
24032425
trap_if(not thread.task.inst.may_leave)
2426+
trap_if(not thread.task.may_block() and not opts.async_)
2427+
24042428
e = thread.task.inst.table.get(i)
24052429
trap_if(not isinstance(e, EndT))
24062430
trap_if(e.shared.t != future_t.t)
@@ -2451,6 +2475,7 @@ def canon_future_cancel_write(future_t, async_, thread, i):
24512475

24522476
def cancel_copy(EndT, event_code, stream_or_future_t, async_, thread, i):
24532477
trap_if(not thread.task.inst.may_leave)
2478+
trap_if(not thread.task.may_block() and not async_)
24542479
e = thread.task.inst.table.get(i)
24552480
trap_if(not isinstance(e, EndT))
24562481
trap_if(e.shared.t != stream_or_future_t.t)
@@ -2527,6 +2552,7 @@ def canon_thread_switch_to(cancellable, thread, i):
25272552

25282553
def canon_thread_suspend(cancellable, thread):
25292554
trap_if(not thread.task.inst.may_leave)
2555+
trap_if(not thread.task.may_block())
25302556
suspend_result = thread.task.suspend(thread, cancellable)
25312557
return [suspend_result]
25322558

@@ -2554,6 +2580,8 @@ def canon_thread_yield_to(cancellable, thread, i):
25542580

25552581
def canon_thread_yield(cancellable, thread):
25562582
trap_if(not thread.task.inst.may_leave)
2583+
if not thread.task.may_block():
2584+
return [SuspendResult.NOT_CANCELLED]
25572585
event_code,_,_ = thread.task.yield_until(lambda: True, thread, cancellable)
25582586
match event_code:
25592587
case EventCode.NONE:

0 commit comments

Comments
 (0)