@@ -584,7 +584,6 @@ re`acquire` the `current_stack` lock to run again.
584
584
``` python
585
585
async def suspend (self , future ):
586
586
assert (current_task.locked())
587
- self .maybe_start_pending_task()
588
587
if self .on_block:
589
588
self .on_block()
590
589
self .on_block = None
@@ -599,16 +598,6 @@ reimplemented using the [`suspend`] instruction of the [typed continuations]
599
598
proposal, removing the need for ` on_block ` and the subtle calling contract
600
599
between ` Task.suspend ` and ` canon_lift ` .
601
600
602
- The ` pending_tasks ` queue (appended to by ` enter ` above) is emptied (by
603
- ` suspend ` above and ` exit ` below) one at a time when backpressure is disabled,
604
- ensuring that each popped tasks gets a chance to start and possibly re-enable
605
- backpressure before the next pending task is started:
606
- ``` python
607
- def maybe_start_pending_task (self ):
608
- if self .inst.pending_tasks and self .may_enter():
609
- self .inst.pending_tasks.pop(0 ).set_result(None )
610
- ```
611
-
612
601
The ` borrow_count ` field is used by the following methods to track the number
613
602
of borrowed handles that were passed as parameters to the export that have not
614
603
yet been dropped (and thus might dangle if the caller destroys the resource
@@ -643,11 +632,25 @@ guarded to be `0` in `Task.exit` (below) to ensure [structured concurrency].
643
632
return
644
633
subtask.enqueued = True
645
634
self .events.put_nowait(subtask)
646
-
635
+ ```
636
+ While a task is running, it may call ` wait ` (via ` canon task.wait ` or, when a
637
+ ` callback ` is present, by returning to the event loop) to block until there is
638
+ progress on one of the task's async subtasks. Although the Python code uses an
639
+ ` asyncio.Queue ` to coordinate async events, an optimized implementation should
640
+ not have to create an actual queue; instead it should be possible to embed a
641
+ "next ready" linked list in the elements of the ` async_subtasks ` table (noting
642
+ the ` enqueued ` guard above ensures that a subtask can be enqueued at most
643
+ once).
644
+ ``` python
647
645
async def wait (self ):
646
+ self .maybe_start_pending_task()
648
647
subtask = await self .suspend(self .events.get())
649
648
return self .process_event(subtask)
650
649
650
+ def maybe_start_pending_task (self ):
651
+ if self .inst.pending_tasks and self .may_enter():
652
+ self .inst.pending_tasks.pop(0 ).set_result(None )
653
+
651
654
def process_event (self , subtask ):
652
655
assert (subtask.supertask is self )
653
656
subtask.enqueued = False
@@ -656,18 +659,14 @@ guarded to be `0` in `Task.exit` (below) to ensure [structured concurrency].
656
659
self .num_async_subtasks -= 1
657
660
return (EventCode(subtask.state), subtask.index)
658
661
```
659
- While a task is running, it may call ` wait ` (via ` canon task.wait ` or, when a
660
- ` callback ` is present, by returning to the event loop) to block until there is
661
- progress on one of the task's async subtasks. Although the Python code above
662
- uses an ` asyncio.Queue ` to coordinate async events, an optimized implementation
663
- should not have to create an actual queue; instead it should be possible to
664
- embed a "next ready" linked list in the elements of the ` async_subtasks ` table
665
- (noting the ` enqueued ` guard above ensures that a subtask can be enqueued at
666
- most once).
662
+ The ` pending_tasks ` queue (appended to by ` enter ` above) is emptied by ` wait `
663
+ (and ` yield_ ` and ` exit ` below) one at a time once backpressure is turned back
664
+ off, ensuring that each popped tasks gets a chance to start and possibly
665
+ re-enable backpressure before the next pending task is started:
667
666
668
- Alternatively, the current task can call ` poll ` (via ` canon task.poll ` , defined
669
- below), which does not block and does not allow the runtime to switch to
670
- another task:
667
+ Instead of ` wait ` ing for a subtask to make progress, the current task can also
668
+ call ` poll ` (via ` canon task.poll ` , defined below), which does not block and
669
+ does not allow the runtime to switch to another task:
671
670
``` python
672
671
def poll (self ):
673
672
if self .events.empty():
@@ -680,6 +679,7 @@ the runtime to switch to another ready task, but without blocking on I/O (as
680
679
emulated in the Python code here by awaiting a ` sleep(0) ` ).
681
680
``` python
682
681
async def yield_ (self ):
682
+ self .maybe_start_pending_task()
683
683
await self .suspend(asyncio.sleep(0 ))
684
684
```
685
685
0 commit comments