Skip to content

Commit fef4107

Browse files
committed
Rename 'thread' to 'active' in CABI and improve wording
1 parent 0f476d9 commit fef4107

File tree

2 files changed

+40
-42
lines changed

2 files changed

+40
-42
lines changed

design/mvp/CanonicalABI.md

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ class ComponentInstance:
261261
pending_async_tasks: list[asyncio.Future]
262262
handles: HandleTables
263263
async_subtasks: Table[AsyncSubtask]
264-
thread: asyncio.Lock
264+
active: asyncio.Lock
265265

266266
def __init__(self):
267267
self.may_leave = True
@@ -271,7 +271,7 @@ class ComponentInstance:
271271
self.pending_async_tasks = []
272272
self.handles = HandleTables()
273273
self.async_subtasks = Table[AsyncSubtask]()
274-
self.thread = asyncio.Lock()
274+
self.active = asyncio.Lock()
275275
```
276276
The `may_leave` field is used below to track whether the instance may call a
277277
lowered import to prevent optimization-breaking cases of reentrance during
@@ -289,11 +289,11 @@ The `async_subtasks` field is used below to track and assign an `i32` index to
289289
each active async-lowered call in progress that has been made by this
290290
`ComponentInstance`.
291291

292-
Finally, the `thread` field is used below to restrict the switching of Python
292+
Finally, the `active` field is used below to restrict the switching of Python
293293
coroutines (`async def` functions) to only occur at specific points (such as
294294
when a task blocks on `task.wait` or when an `async callback`-lifted export
295295
call returns to its event loop to wait for an event. Thus, the calls to
296-
`thread.acquire()` and `thread.release()` in the Python code below point to
296+
`active.acquire()` and `active.release()` in the Python code below point to
297297
where the runtime may switch between concurrent tasks. Without this
298298
`asyncio.Lock`, Python's normal `asyncio` semantics would allow switching
299299
between concurrent tasks at *every* spec-internal `await`, which would lead to
@@ -495,12 +495,12 @@ for a component export called directly by the host, or else the current task
495495
when the calling component called into this component. The `caller` field is
496496
used by the following two methods to prevent a component from being reentered
497497
(enforcing the [component invariant]) in a way that is well-defined even in the
498-
presence of async calls). (The `thread.acquire()` call in `enter()` is
498+
presence of async calls). (The `active.acquire()` call in `enter()` is
499499
described above and here ensures that concurrent export calls do not
500500
arbitrarily interleave.)
501501
```python
502502
async def enter(self):
503-
await self.inst.thread.acquire()
503+
await self.inst.active.acquire()
504504
self.trap_if_on_the_stack(self.inst)
505505

506506
def trap_if_on_the_stack(self, inst):
@@ -558,9 +558,9 @@ guarded to be `0` in `Task.exit` (below) to ensure [structured concurrency].
558558
self.events.put_nowait(subtask)
559559

560560
async def wait(self):
561-
self.inst.thread.release()
561+
self.inst.active.release()
562562
subtask = await self.events.get()
563-
await self.inst.thread.acquire()
563+
await self.inst.active.acquire()
564564
return self.process_event(subtask)
565565

566566
def process_event(self, subtask):
@@ -579,7 +579,7 @@ should not have to create an actual queue; instead it should be possible to
579579
embed a "next ready" linked list in the elements of the `async_subtasks` table
580580
(noting the `enqueued` guard above ensures that a subtask can be enqueued at
581581
most once). The implementation of `wait` releases and reacquires the
582-
instance-wide `thread` lock to specify that `wait` is a point where the runtime
582+
instance-wide `active` lock to specify that `wait` is a point where the runtime
583583
can switch to another task running in the same component instance or start a
584584
new task in response to an incoming export call.
585585

@@ -598,20 +598,20 @@ the runtime to switch to another ready task, but without blocking on I/O (as
598598
emulated in the Python code here by awaiting a `sleep(0)`).
599599
```python
600600
async def yield_(self):
601-
self.inst.thread.release()
601+
self.inst.active.release()
602602
await asyncio.sleep(0)
603-
await self.inst.thread.acquire()
603+
await self.inst.active.acquire()
604604
```
605605

606606
Lastly, when a task exists, the runtime enforces the guard conditions mentioned
607-
above and releases the `thread` lock, allowing other tasks to start or make
607+
above and releases the `active` lock, allowing other tasks to start or make
608608
progress.
609609
```python
610610
def exit(self):
611611
assert(self.events.empty())
612612
trap_if(self.borrow_count != 0)
613613
trap_if(self.num_async_subtasks != 0)
614-
self.inst.thread.release()
614+
self.inst.active.release()
615615
```
616616

617617
While `canon_lift` creates `Task`s, `canon_lower` creates `Subtask` objects:
@@ -2077,8 +2077,8 @@ async def canon_lower(opts, callee, ft, task, flat_args):
20772077
await callee(task, start_thunk, return_thunk)
20782078
subtask.finish()
20792079

2080+
asyncio.get_event_loop().set_task_factory(asyncio.eager_task_factory)
20802081
asyncio.create_task(do_call())
2081-
await asyncio.sleep(0) # start do_call eagerly
20822082

20832083
if subtask.state == AsyncCallState.DONE:
20842084
flat_results = [0]
@@ -2090,23 +2090,22 @@ async def canon_lower(opts, callee, ft, task, flat_args):
20902090

20912091
return flat_results
20922092
```
2093-
In the async case, `asyncio.create_task` followed by `await asyncio.sleep(0)`
2094-
are used together to achieve the effect of eagerly executing the `do_call`
2095-
Python coroutine without `await`ing it. Following the `sleep(0)`, the coroutine
2096-
has either completed eagerly (with `return_thunk` having been called to write
2097-
the return values into the caller-supplied memory buffer), in which case the
2098-
lowered function can simply return `0` to indicate "done". Otherwise, the
2099-
coroutine is still running, in which case it is added to an instance-wide table
2100-
of active async subtasks, returning the table index packed with the current
2101-
state of the subtask (so that the caller can know whether it can reclaim the
2102-
parameter and result memory buffers) and delivering subsequent progress events
2103-
to the calling task via the `AsyncSubtask` `start` and `return_` methods
2104-
(defined above).
2105-
2106-
Note that the async case does *not* release or reacquire the `thread` lock
2093+
In the async case, the combination of `asyncio.create_task` with
2094+
`eager_task_factory` immediately start executing `do_call` without `await`ing
2095+
it. Following `create_task`, `do_call` has either completed eagerly (with
2096+
`return_thunk` having been called to write the return values into the
2097+
caller-supplied memory buffer), in which case the lowered function can simply
2098+
return `0` to indicate "done". Otherwise, the coroutine is still running, in
2099+
which case it is added to an instance-wide table of active async subtasks,
2100+
returning the table index packed with the current state of the subtask (so that
2101+
the caller can know whether it can reclaim the parameter and result memory
2102+
buffers) and delivering subsequent progress events to the calling task via the
2103+
`AsyncSubtask` `start` and `return_` methods (defined above).
2104+
2105+
Note that the async case does *not* release or reacquire the `active` lock
21072106
since (due to the `trap_if_on_stack` reentrance guard in `Task.enter`) the
21082107
`callee` is necessarily in another component instance (which has a separate
2109-
`thread` lock). This allows fine-grained inter-component task interleaving (up
2108+
`active` lock). This allows fine-grained inter-component task interleaving (up
21102109
to and including preemptive multithreading) which doesn't break regular async
21112110
codes' assumptions due to the component shared-nothing model. This also means
21122111
that an async import calls are *not* allowed to switch to another task in the
@@ -2314,7 +2313,7 @@ async def canon_task_wait(task, ptr):
23142313
The `trap_if` ensures that, when a component uses a `callback` all events flow
23152314
through the event loop at the base of the stack.
23162315

2317-
Note that `task.wait` releases and reacquires the `thread` lock and thus
2316+
Note that `task.wait` releases and reacquires the `active` lock and thus
23182317
`canon_task_wait` allows the runtime to switch to another active task in the
23192318
current component instance. Note also that `task.wait` can be called from a
23202319
sync-lifted `SyncTask` so that even fully synchronous code can make concurrent
@@ -2354,9 +2353,8 @@ For a canonical definition:
23542353
validation specifies:
23552354
* `$f` is given type `(func)`
23562355

2357-
Calling `$f` simply releases and reacquires the `thread` lock, using a
2358-
Python `asyncio.sleep(0)` in the middle to make it clear that other
2359-
coroutines are allowed to acquire the `lock` and execute.
2356+
Calling `$f` calls `Task.yield_`, trapping if called when there is a `callback`.
2357+
(When there is a callback, yielding is achieved by returning with the LSB set.)
23602358
```python
23612359
async def canon_task_yield(task):
23622360
trap_if(task.opts.callback is not None)

design/mvp/canonical-abi/definitions.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ class ComponentInstance:
299299
pending_async_tasks: list[asyncio.Future]
300300
handles: HandleTables
301301
async_subtasks: Table[AsyncSubtask]
302-
thread: asyncio.Lock
302+
active: asyncio.Lock
303303

304304
def __init__(self):
305305
self.may_leave = True
@@ -309,7 +309,7 @@ def __init__(self):
309309
self.pending_async_tasks = []
310310
self.handles = HandleTables()
311311
self.async_subtasks = Table[AsyncSubtask]()
312-
self.thread = asyncio.Lock()
312+
self.active = asyncio.Lock()
313313

314314
class HandleTables:
315315
rt_to_table: MutableMapping[ResourceType, Table[HandleElem]]
@@ -411,7 +411,7 @@ def __init__(self, opts, inst, caller):
411411
self.num_async_subtasks = 0
412412

413413
async def enter(self):
414-
await self.inst.thread.acquire()
414+
await self.inst.active.acquire()
415415
self.trap_if_on_the_stack(self.inst)
416416

417417
def trap_if_on_the_stack(self, inst):
@@ -442,9 +442,9 @@ def async_subtask_made_progress(self, subtask):
442442
self.events.put_nowait(subtask)
443443

444444
async def wait(self):
445-
self.inst.thread.release()
445+
self.inst.active.release()
446446
subtask = await self.events.get()
447-
await self.inst.thread.acquire()
447+
await self.inst.active.acquire()
448448
return self.process_event(subtask)
449449

450450
def process_event(self, subtask):
@@ -461,15 +461,15 @@ def poll(self):
461461
return self.process_event(self.events.get_nowait())
462462

463463
async def yield_(self):
464-
self.inst.thread.release()
464+
self.inst.active.release()
465465
await asyncio.sleep(0)
466-
await self.inst.thread.acquire()
466+
await self.inst.active.acquire()
467467

468468
def exit(self):
469469
assert(self.events.empty())
470470
trap_if(self.borrow_count != 0)
471471
trap_if(self.num_async_subtasks != 0)
472-
self.inst.thread.release()
472+
self.inst.active.release()
473473

474474
class Subtask(CallContext):
475475
lenders: list[HandleElem]
@@ -1435,8 +1435,8 @@ def return_thunk(results):
14351435
await callee(task, start_thunk, return_thunk)
14361436
subtask.finish()
14371437

1438+
asyncio.get_event_loop().set_task_factory(asyncio.eager_task_factory)
14381439
asyncio.create_task(do_call())
1439-
await asyncio.sleep(0) # start do_call eagerly
14401440

14411441
if subtask.state == AsyncCallState.DONE:
14421442
flat_results = [0]

0 commit comments

Comments
 (0)