@@ -261,7 +261,7 @@ class ComponentInstance:
261
261
pending_async_tasks: list[asyncio.Future]
262
262
handles: HandleTables
263
263
async_subtasks: Table[AsyncSubtask]
264
- thread : asyncio.Lock
264
+ active : asyncio.Lock
265
265
266
266
def __init__ (self ):
267
267
self .may_leave = True
@@ -271,7 +271,7 @@ class ComponentInstance:
271
271
self .pending_async_tasks = []
272
272
self .handles = HandleTables()
273
273
self .async_subtasks = Table[AsyncSubtask]()
274
- self .thread = asyncio.Lock()
274
+ self .active = asyncio.Lock()
275
275
```
276
276
The ` may_leave ` field is used below to track whether the instance may call a
277
277
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
289
289
each active async-lowered call in progress that has been made by this
290
290
` ComponentInstance ` .
291
291
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
293
293
coroutines (` async def ` functions) to only occur at specific points (such as
294
294
when a task blocks on ` task.wait ` or when an ` async callback ` -lifted export
295
295
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
297
297
where the runtime may switch between concurrent tasks. Without this
298
298
` asyncio.Lock ` , Python's normal ` asyncio ` semantics would allow switching
299
299
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
495
495
when the calling component called into this component. The ` caller ` field is
496
496
used by the following two methods to prevent a component from being reentered
497
497
(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
499
499
described above and here ensures that concurrent export calls do not
500
500
arbitrarily interleave.)
501
501
``` python
502
502
async def enter (self ):
503
- await self .inst.thread .acquire()
503
+ await self .inst.active .acquire()
504
504
self .trap_if_on_the_stack(self .inst)
505
505
506
506
def trap_if_on_the_stack (self , inst ):
@@ -558,9 +558,9 @@ guarded to be `0` in `Task.exit` (below) to ensure [structured concurrency].
558
558
self .events.put_nowait(subtask)
559
559
560
560
async def wait (self ):
561
- self .inst.thread .release()
561
+ self .inst.active .release()
562
562
subtask = await self .events.get()
563
- await self .inst.thread .acquire()
563
+ await self .inst.active .acquire()
564
564
return self .process_event(subtask)
565
565
566
566
def process_event (self , subtask ):
@@ -579,7 +579,7 @@ should not have to create an actual queue; instead it should be possible to
579
579
embed a "next ready" linked list in the elements of the ` async_subtasks ` table
580
580
(noting the ` enqueued ` guard above ensures that a subtask can be enqueued at
581
581
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
583
583
can switch to another task running in the same component instance or start a
584
584
new task in response to an incoming export call.
585
585
@@ -598,20 +598,20 @@ the runtime to switch to another ready task, but without blocking on I/O (as
598
598
emulated in the Python code here by awaiting a ` sleep(0) ` ).
599
599
``` python
600
600
async def yield_ (self ):
601
- self .inst.thread .release()
601
+ self .inst.active .release()
602
602
await asyncio.sleep(0 )
603
- await self .inst.thread .acquire()
603
+ await self .inst.active .acquire()
604
604
```
605
605
606
606
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
608
608
progress.
609
609
``` python
610
610
def exit (self ):
611
611
assert (self .events.empty())
612
612
trap_if(self .borrow_count != 0 )
613
613
trap_if(self .num_async_subtasks != 0 )
614
- self .inst.thread .release()
614
+ self .inst.active .release()
615
615
```
616
616
617
617
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):
2077
2077
await callee(task, start_thunk, return_thunk)
2078
2078
subtask.finish()
2079
2079
2080
+ asyncio.get_event_loop().set_task_factory(asyncio.eager_task_factory)
2080
2081
asyncio.create_task(do_call())
2081
- await asyncio.sleep(0 ) # start do_call eagerly
2082
2082
2083
2083
if subtask.state == AsyncCallState.DONE :
2084
2084
flat_results = [0 ]
@@ -2090,23 +2090,22 @@ async def canon_lower(opts, callee, ft, task, flat_args):
2090
2090
2091
2091
return flat_results
2092
2092
```
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
2107
2106
since (due to the ` trap_if_on_stack ` reentrance guard in ` Task.enter ` ) the
2108
2107
` 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
2110
2109
to and including preemptive multithreading) which doesn't break regular async
2111
2110
codes' assumptions due to the component shared-nothing model. This also means
2112
2111
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):
2314
2313
The ` trap_if ` ensures that, when a component uses a ` callback ` all events flow
2315
2314
through the event loop at the base of the stack.
2316
2315
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
2318
2317
` canon_task_wait ` allows the runtime to switch to another active task in the
2319
2318
current component instance. Note also that ` task.wait ` can be called from a
2320
2319
sync-lifted ` SyncTask ` so that even fully synchronous code can make concurrent
@@ -2354,9 +2353,8 @@ For a canonical definition:
2354
2353
validation specifies:
2355
2354
* ` $f ` is given type ` (func) `
2356
2355
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.)
2360
2358
``` python
2361
2359
async def canon_task_yield (task ):
2362
2360
trap_if(task.opts.callback is not None )
0 commit comments