Skip to content

Commit 1072441

Browse files
committed
Add cooperative threads
1 parent b5ace5f commit 1072441

2 files changed

Lines changed: 76 additions & 118 deletions

File tree

design/mvp/CanonicalABI.md

Lines changed: 44 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ being specified here.
3737
* [`canon resource.new`](#canon-resourcenew)
3838
* [`canon resource.drop`](#canon-resourcedrop)
3939
* [`canon resource.rep`](#canon-resourcerep)
40+
* [`canon thread.new_indirect`](#-canon-threadnew_indirect) 🧵
4041
* [`canon context.get`](#-canon-contextget) 🔀
4142
* [`canon context.set`](#-canon-contextset) 🔀
4243
* [`canon backpressure.set`](#-canon-backpressureset) 🔀
@@ -58,9 +59,7 @@ being specified here.
5859
* [`canon error-context.new`](#-canon-error-contextnew) 📝
5960
* [`canon error-context.debug-message`](#-canon-error-contextdebug-message) 📝
6061
* [`canon error-context.drop`](#-canon-error-contextdrop) 📝
61-
* [`canon thread.spawn_ref`](#-canon-threadspawn_ref) 🧵
62-
* [`canon thread.spawn_indirect`](#-canon-threadspawn_indirect) 🧵
63-
* [`canon thread.available_parallelism`](#-canon-threadavailable_parallelism) 🧵
62+
* [`canon thread.available_parallelism`](#-canon-threadavailable_parallelism)
6463

6564
## Supporting definitions
6665

@@ -3419,6 +3418,46 @@ Note that the "locally-defined" requirement above ensures that only the
34193418
component instance defining a resource can access its representation.
34203419

34213420

3421+
### 🧵 `canon thread.new_indirect`
3422+
3423+
TODO: rewrite
3424+
3425+
For a canonical definition:
3426+
```wat
3427+
(canon thread.new_indirect $shared? $ft $tbl (core func $f))
3428+
```
3429+
validation specifies:
3430+
* `$ft` must refer to a `shared` function type; initially, only the type
3431+
`(shared (func (param $c i32)))` is allowed (see explanation in
3432+
`thread.spawn_ref` above)
3433+
* `$tbl` must refer to a shared table whose element type matches `(ref null
3434+
(shared func))`
3435+
* `$spawn_indirect` is given type `(func (param $i i32) (param $c i32) (result
3436+
$e i32))`.
3437+
3438+
Calling `$spawn_indirect` retrieves a reference to function `$f` from table
3439+
`$tbl` and checks that `$f` is of type `$ft`. If that succeeds, it spawns a
3440+
thread which:
3441+
- invokes `$f` with `$c`
3442+
- executes `$f` until completion or trap in a `shared` context as described by
3443+
the [shared-everything threads] proposal.
3444+
3445+
In pseudocode, `$spawn_indirect` looks like:
3446+
3447+
```python
3448+
async def canon_thread_new_indirect(shared, ft, ftbl, thread, i, c):
3449+
inst = thread.task.inst
3450+
trap_if(not inst.may_leave)
3451+
assert(not shared)
3452+
f = ftbl.get(i)
3453+
trap_if(f is None)
3454+
trap_if(f.type != ft)
3455+
new_thread = Thread(thread.task, f(c))
3456+
return [inst.table.add(thread)]
3457+
```
3458+
TODO
3459+
3460+
34223461
### 🔀 `canon context.get`
34233462

34243463
For a canonical definition:
@@ -4226,6 +4265,7 @@ async def canon_error_context_debug_message(opts, task, i, ptr):
42264265
Note that `ptr` points to an 8-byte region of memory into which will be stored
42274266
the pointer and length of the debug string (allocated via `opts.realloc`).
42284267

4268+
42294269
### 📝 `canon error-context.drop`
42304270

42314271
For a canonical definition:
@@ -4246,99 +4286,7 @@ async def canon_error_context_drop(task, i):
42464286
```
42474287

42484288

4249-
### 🧵 `canon thread.spawn_ref`
4250-
4251-
For a canonical definition:
4252-
```wat
4253-
(canon thread.spawn_ref $ft (core func $spawn_ref))
4254-
```
4255-
validation specifies:
4256-
* `$ft` must refer to a `shared` function type; initially, only the type
4257-
`(shared (func (param $c i32)))` is allowed (see explanation below)
4258-
* `$spawn_ref` is given type `(func (param $f (ref null $ft)) (param $c i32)
4259-
(result $e i32))`.
4260-
4261-
> Note: ideally, a thread could be spawned with [arbitrary thread parameters].
4262-
> Currently, that would require additional work in the toolchain to support so,
4263-
> for simplicity, the current proposal simply fixes a single `i32` parameter
4264-
> type. However, `thread.spawn_ref` could be extended to allow arbitrary thread
4265-
> parameters in the future, once it's concretely beneficial to the toolchain.
4266-
> The inclusion of `$ft` ensures backwards compatibility for when arbitrary
4267-
> parameters are allowed.
4268-
4269-
Calling `$spawn_ref` checks that the reference `$f` is not null. Then, it spawns
4270-
a thread which:
4271-
- invokes `$f` with `$c`
4272-
- executes `$f` until completion or trap in a `shared` context as described by
4273-
the [shared-everything threads] proposal.
4274-
4275-
In pseudocode, `$spawn_ref` looks like:
4276-
4277-
```python
4278-
def canon_thread_spawn_ref(f, c):
4279-
trap_if(f is None)
4280-
if DETERMINISTIC_PROFILE:
4281-
return [-1]
4282-
4283-
def thread_start():
4284-
try:
4285-
f(c)
4286-
except CoreWebAssemblyException:
4287-
trap()
4288-
4289-
if spawn(thread_start):
4290-
return [0]
4291-
else:
4292-
return [-1]
4293-
```
4294-
4295-
4296-
### 🧵 `canon thread.spawn_indirect`
4297-
4298-
For a canonical definition:
4299-
```wat
4300-
(canon thread.spawn_indirect $ft $tbl (core func $spawn_indirect))
4301-
```
4302-
validation specifies:
4303-
* `$ft` must refer to a `shared` function type; initially, only the type
4304-
`(shared (func (param $c i32)))` is allowed (see explanation in
4305-
`thread.spawn_ref` above)
4306-
* `$tbl` must refer to a shared table whose element type matches `(ref null
4307-
(shared func))`
4308-
* `$spawn_indirect` is given type `(func (param $i i32) (param $c i32) (result
4309-
$e i32))`.
4310-
4311-
Calling `$spawn_indirect` retrieves a reference to function `$f` from table
4312-
`$tbl` and checks that `$f` is of type `$ft`. If that succeeds, it spawns a
4313-
thread which:
4314-
- invokes `$f` with `$c`
4315-
- executes `$f` until completion or trap in a `shared` context as described by
4316-
the [shared-everything threads] proposal.
4317-
4318-
In pseudocode, `$spawn_indirect` looks like:
4319-
4320-
```python
4321-
def canon_thread_spawn_indirect(ft, tbl, i, c):
4322-
f = tbl[i]
4323-
trap_if(f is None)
4324-
trap_if(f.type != ft)
4325-
if DETERMINISTIC_PROFILE:
4326-
return [-1]
4327-
4328-
def thread_start():
4329-
try:
4330-
f(c)
4331-
except CoreWebAssemblyException:
4332-
trap()
4333-
4334-
if spawn(thread_start):
4335-
return [0]
4336-
else:
4337-
return [-1]
4338-
```
4339-
4340-
4341-
### 🧵 `canon thread.available_parallelism`
4289+
### `canon thread.available_parallelism`
43424290

43434291
For a canonical definition:
43444292
```wat

design/mvp/canonical-abi/definitions.py

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -358,22 +358,6 @@ def write(self, vs):
358358
assert(all(v == () for v in vs))
359359
self.progress += len(vs)
360360

361-
#### Context-Local Storage
362-
363-
class ContextLocalStorage:
364-
LENGTH = 1
365-
array: list[int]
366-
367-
def __init__(self):
368-
self.array = [0] * ContextLocalStorage.LENGTH
369-
370-
def set(self, i, v):
371-
assert(types_match_values(['i32'], [v]))
372-
self.array[i] = v
373-
374-
def get(self, i):
375-
return self.array[i]
376-
377361
#### Waitable State
378362

379363
class EventCode(IntEnum):
@@ -475,7 +459,6 @@ class State(Enum):
475459
supertask: Optional[Task]
476460
on_resolve: Callable[[Optional[list[any]]], None]
477461
num_borrows: int
478-
context: ContextLocalStorage
479462

480463
def __init__(self, opts, inst, ft, supertask, on_resolve):
481464
self.state = Task.State.INITIAL
@@ -485,7 +468,6 @@ def __init__(self, opts, inst, ft, supertask, on_resolve):
485468
self.supertask = supertask
486469
self.on_resolve = on_resolve
487470
self.num_borrows = 0
488-
self.context = ContextLocalStorage()
489471

490472
async def enter(self, thread):
491473
self.trap_if_on_the_stack(self.inst)
@@ -889,12 +871,16 @@ def drop(self):
889871

890872
class Thread:
891873
task: Task
874+
context: list[int]
892875
awaitable: Optional[Awaitable]
893876
on_resume: Optional[asyncio.Future]
894877
on_suspend_or_exit: Optional[asyncio.Future]
895878

879+
CONTEXT_LENGTH = 1
880+
896881
def __init__(self, task, coro):
897882
self.task = task
883+
self.context = [0] * Thread.CONTEXT_LENGTH
898884
self.awaitable = asyncio.Future()
899885
self.on_resume = asyncio.Future()
900886
self.on_suspend_or_exit = None
@@ -930,6 +916,18 @@ async def suspend(self, awaitable) -> Cancelled:
930916
self.on_resume = None
931917
return cancelled
932918

919+
async def switch(self, other: Thread) -> Cancelled:
920+
assert(not self.awaitable and not other.awaitable)
921+
assert(self.on_suspend_or_exit and not other.on_suspend_or_exit)
922+
other.on_suspend_or_exit = self.on_suspend_or_exit
923+
self.on_suspend_or_exit = None
924+
other.on_resume.set_result(Cancelled.FALSE)
925+
assert(not self.on_resume)
926+
self.on_resume = asyncio.Future()
927+
cancelled = await self.on_resume
928+
self.on_resume = None
929+
return cancelled
930+
933931
#### Store State / Embedding API
934932

935933
class Store:
@@ -2108,19 +2106,31 @@ async def canon_resource_rep(rt, thread, i):
21082106
trap_if(h.rt is not rt)
21092107
return [h.rep]
21102108

2109+
### 🧵 `canon thread.new_indirect`
2110+
2111+
async def canon_thread_new_indirect(shared, ft, ftbl, thread, i, c):
2112+
inst = thread.task.inst
2113+
trap_if(not inst.may_leave)
2114+
assert(not shared)
2115+
f = ftbl.get(i)
2116+
trap_if(f is None)
2117+
trap_if(f.type != ft)
2118+
new_thread = Thread(thread.task, f(c))
2119+
return [inst.table.add(thread)]
2120+
21112121
### 🔀 `canon context.get`
21122122

21132123
async def canon_context_get(t, i, thread):
21142124
assert(t == 'i32')
2115-
assert(i < ContextLocalStorage.LENGTH)
2116-
return [thread.task.context.get(i)]
2125+
assert(i < Thread.CONTEXT_LENGTH)
2126+
return [thread.context[i]]
21172127

21182128
### 🔀 `canon context.set`
21192129

21202130
async def canon_context_set(t, i, thread, v):
21212131
assert(t == 'i32')
2122-
assert(i < ContextLocalStorage.LENGTH)
2123-
thread.task.context.set(i, v)
2132+
assert(i < Thread.CONTEXT_LENGTH)
2133+
thread.context[i] = v
21242134
return []
21252135

21262136
### 🔀 `canon backpressure.set`

0 commit comments

Comments
 (0)