@@ -584,7 +584,6 @@ re`acquire` the `current_stack` lock to run again.
584584``` python
585585 async def suspend (self , future ):
586586 assert (current_task.locked())
587- self .maybe_start_pending_task()
588587 if self .on_block:
589588 self .on_block()
590589 self .on_block = None
@@ -599,16 +598,6 @@ reimplemented using the [`suspend`] instruction of the [typed continuations]
599598proposal, removing the need for ` on_block ` and the subtle calling contract
600599between ` Task.suspend ` and ` canon_lift ` .
601600
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-
612601The ` borrow_count ` field is used by the following methods to track the number
613602of borrowed handles that were passed as parameters to the export that have not
614603yet 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].
643632 return
644633 subtask.enqueued = True
645634 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
647645 async def wait (self ):
646+ self .maybe_start_pending_task()
648647 subtask = await self .suspend(self .events.get())
649648 return self .process_event(subtask)
650649
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+
651654 def process_event (self , subtask ):
652655 assert (subtask.supertask is self )
653656 subtask.enqueued = False
@@ -656,18 +659,14 @@ guarded to be `0` in `Task.exit` (below) to ensure [structured concurrency].
656659 self .num_async_subtasks -= 1
657660 return (EventCode(subtask.state), subtask.index)
658661```
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:
667666
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:
671670``` python
672671 def poll (self ):
673672 if self .events.empty():
@@ -680,6 +679,7 @@ the runtime to switch to another ready task, but without blocking on I/O (as
680679emulated in the Python code here by awaiting a ` sleep(0) ` ).
681680``` python
682681 async def yield_ (self ):
682+ self .maybe_start_pending_task()
683683 await self .suspend(asyncio.sleep(0 ))
684684```
685685
0 commit comments