Skip to content

Commit 655d5ac

Browse files
committed
Refactor: move maybe_start_pending_task to wait/yield for simplicity
1 parent 4eb255f commit 655d5ac

File tree

2 files changed

+29
-28
lines changed

2 files changed

+29
-28
lines changed

design/mvp/CanonicalABI.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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]
599598
proposal, removing the need for `on_block` and the subtle calling contract
600599
between `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-
612601
The `borrow_count` field is used by the following methods to track the number
613602
of borrowed handles that were passed as parameters to the export that have not
614603
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].
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
680679
emulated 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

design/mvp/canonical-abi/definitions.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,6 @@ def may_enter(self):
440440

441441
async def suspend(self, future):
442442
assert(current_task.locked())
443-
self.maybe_start_pending_task()
444443
if self.on_block:
445444
self.on_block()
446445
self.on_block = None
@@ -450,10 +449,6 @@ async def suspend(self, future):
450449
await current_task.acquire()
451450
return r
452451

453-
def maybe_start_pending_task(self):
454-
if self.inst.pending_tasks and self.may_enter():
455-
self.inst.pending_tasks.pop(0).set_result(None)
456-
457452
def create_borrow(self):
458453
self.borrow_count += 1
459454

@@ -476,9 +471,14 @@ def async_subtask_made_progress(self, subtask):
476471
self.events.put_nowait(subtask)
477472

478473
async def wait(self):
474+
self.maybe_start_pending_task()
479475
subtask = await self.suspend(self.events.get())
480476
return self.process_event(subtask)
481477

478+
def maybe_start_pending_task(self):
479+
if self.inst.pending_tasks and self.may_enter():
480+
self.inst.pending_tasks.pop(0).set_result(None)
481+
482482
def process_event(self, subtask):
483483
assert(subtask.supertask is self)
484484
subtask.enqueued = False
@@ -493,6 +493,7 @@ def poll(self):
493493
return self.process_event(self.events.get_nowait())
494494

495495
async def yield_(self):
496+
self.maybe_start_pending_task()
496497
await self.suspend(asyncio.sleep(0))
497498

498499
def exit(self):

0 commit comments

Comments
 (0)