Skip to content

Commit de20c36

Browse files
committed
asyncio: Properly cancel the main task on exception.
If the main task is interrupted by e.g. a KeyboardInterrupt, then the main task needs to have the exception injected into it so it will run the exception handlers and contextmanager __aexit__ methods. Additionally, if an error is encountered in the poll loop, it will be injected into the main task. Signed-off-by: Jared Hancock <jared@greezybacon.me>
1 parent 8159dcc commit de20c36

File tree

1 file changed

+36
-22
lines changed

1 file changed

+36
-22
lines changed

extmod/asyncio/core.py

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from time import ticks_ms as ticks, ticks_diff, ticks_add
55
import sys, select
6+
from select import POLLIN, POLLOUT
67

78
# Import TaskQueue and Task, preferring built-in C code over Python code
89
try:
@@ -54,7 +55,8 @@ def __next__(self):
5455
# Use a SingletonGenerator to do it without allocating on the heap
5556
def sleep_ms(t, sgen=SingletonGenerator()):
5657
assert sgen.state is None
57-
sgen.state = ticks_add(ticks(), max(0, t))
58+
now = ticks()
59+
sgen.state = ticks_add(now, t) if t > 0 else now
5860
return sgen
5961

6062

@@ -77,13 +79,13 @@ def _enqueue(self, s, idx):
7779
entry = [None, None, s]
7880
entry[idx] = cur_task
7981
self.map[id(s)] = entry
80-
self.poller.register(s, select.POLLIN if idx == 0 else select.POLLOUT)
82+
self.poller.register(s, POLLIN if idx == 0 else POLLOUT)
8183
else:
8284
sm = self.map[id(s)]
8385
assert sm[idx] is None
8486
assert sm[1 - idx] is not None
8587
sm[idx] = cur_task
86-
self.poller.modify(s, select.POLLIN | select.POLLOUT)
88+
self.poller.modify(s, POLLIN | POLLOUT)
8789
# Link task to this IOQueue so it can be removed if needed
8890
cur_task.data = self
8991

@@ -114,20 +116,20 @@ def wait_io_event(self, dt):
114116
for s, ev in self.poller.ipoll(dt):
115117
sm = self.map[id(s)]
116118
# print('poll', s, sm, ev)
117-
if ev & ~select.POLLOUT and sm[0] is not None:
119+
if ev & ~POLLOUT and sm[0] is not None:
118120
# POLLIN or error
119121
_task_queue.push(sm[0])
120122
sm[0] = None
121-
if ev & ~select.POLLIN and sm[1] is not None:
123+
if ev & ~POLLIN and sm[1] is not None:
122124
# POLLOUT or error
123125
_task_queue.push(sm[1])
124126
sm[1] = None
125127
if sm[0] is None and sm[1] is None:
126128
self._dequeue(s)
127129
elif sm[0] is None:
128-
self.poller.modify(s, select.POLLOUT)
130+
self.poller.modify(s, POLLOUT)
129131
else:
130-
self.poller.modify(s, select.POLLIN)
132+
self.poller.modify(s, POLLIN)
131133

132134

133135
################################################################################
@@ -153,24 +155,36 @@ def run_until_complete(main_task=None):
153155
global cur_task
154156
excs_all = (CancelledError, Exception) # To prevent heap allocation in loop
155157
excs_stop = (CancelledError, StopIteration) # To prevent heap allocation in loop
158+
queue_peek = _task_queue.peek
159+
queue_pop = _task_queue.pop
160+
wait_io_event = _io_queue.wait_io_event
156161
while True:
157-
# Wait until the head of _task_queue is ready to run
158-
dt = 1
159-
while dt > 0:
160-
dt = -1
161-
t = _task_queue.peek()
162-
if t:
163-
# A task waiting on _task_queue; "ph_key" is time to schedule task at
164-
dt = max(0, ticks_diff(t.ph_key, ticks()))
165-
elif not _io_queue.map:
166-
# No tasks can be woken so finished running
167-
cur_task = None
168-
return
169-
# print('(poll {})'.format(dt), len(_io_queue.map))
170-
_io_queue.wait_io_event(dt)
162+
try:
163+
# Wait until the head of _task_queue is ready to run
164+
dt = 1
165+
while dt > 0:
166+
dt = -1
167+
t = queue_peek()
168+
if t:
169+
# A task waiting on _task_queue; "ph_key" is time to schedule task at
170+
dt = ticks_diff(t.ph_key, ticks())
171+
dt = dt if dt > 0 else 0
172+
elif not _io_queue.map:
173+
# No tasks can be woken so finished running
174+
cur_task = None
175+
return
176+
# print('(poll {})'.format(dt), len(_io_queue.map))
177+
wait_io_event(dt)
178+
except BaseException as exc:
179+
try:
180+
if main_task:
181+
main_task.coro.throw(exc)
182+
except StopIteration:
183+
pass
184+
raise
171185

172186
# Get next task to run and continue it
173-
t = _task_queue.pop()
187+
t = queue_pop()
174188
cur_task = t
175189
try:
176190
# Continue running the coroutine, it's responsible for rescheduling itself

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy