Fixed bugs with cancellations and exceptions
This commit is contained in:
parent
98a280ce39
commit
724b5c98ba
|
@ -166,6 +166,8 @@ async def cancel(task: Task, block: bool = False):
|
|||
|
||||
if task.done():
|
||||
return
|
||||
if task.scope and not task.scope.cancellable:
|
||||
return
|
||||
await syscall("cancel", task)
|
||||
if block:
|
||||
await wait(task)
|
||||
|
|
|
@ -126,10 +126,12 @@ class FIFOKernel:
|
|||
:return:
|
||||
"""
|
||||
|
||||
self._sigint_handled = True
|
||||
if not currently_protected():
|
||||
self.run_ready.appendleft(self.entry_point)
|
||||
self.entry_point.pending_exception = KeyboardInterrupt()
|
||||
self.handle_errors(self.run_task_step)
|
||||
else:
|
||||
self._sigint_handled = True
|
||||
|
||||
def done(self) -> bool:
|
||||
"""
|
||||
|
@ -352,28 +354,17 @@ class FIFOKernel:
|
|||
# there are no more runnable tasks
|
||||
return
|
||||
self.current_task = self.run_ready.popleft()
|
||||
if self.current_task.pending_cancellation:
|
||||
# We perform the deferred cancellation
|
||||
# if it was previously scheduled
|
||||
self.cancel(self.current_task)
|
||||
if self.current_task.done():
|
||||
return
|
||||
self._running = True
|
||||
_runner = self.current_task.run
|
||||
_data = [self.data.pop(self.current_task, None)]
|
||||
if exc := self.current_task.pending_exception:
|
||||
self.current_task.pending_exception = None
|
||||
_runner = partial(self.current_task.throw, exc)
|
||||
_data = []
|
||||
# Some debugging and internal chatter here
|
||||
self.current_task.steps += 1
|
||||
self.current_task.state = TaskState.RUN
|
||||
self.debugger.before_task_step(self.current_task)
|
||||
if self._sigint_handled:
|
||||
self._sigint_handled = False
|
||||
self.current_task.throw(KeyboardInterrupt())
|
||||
_runner = partial(self.current_task.throw, KeyboardInterrupt)
|
||||
_data = []
|
||||
elif exc := self.current_task.pending_exception:
|
||||
self.current_task.pending_exception = None
|
||||
_runner = partial(self.current_task.throw, exc)
|
||||
_data = []
|
||||
# Run a single step with the calculation (i.e. until a yield
|
||||
# somewhere)
|
||||
method, args, kwargs = _runner(*_data)
|
||||
|
@ -456,7 +447,9 @@ class FIFOKernel:
|
|||
else:
|
||||
task = self.current_task
|
||||
"""
|
||||
self._sigint_handled = False
|
||||
task = self.entry_point
|
||||
task.pending_exception = KeyboardInterrupt()
|
||||
self.run_ready.appendleft(task)
|
||||
self.handle_errors(self.run_task_step)
|
||||
elif not self.run_ready:
|
||||
|
@ -579,14 +572,14 @@ class FIFOKernel:
|
|||
it fails
|
||||
"""
|
||||
|
||||
if not task.scope or task.scope.cancellable:
|
||||
self.paused.discard(task)
|
||||
self.io_release_task(task)
|
||||
self.run_ready.append(task)
|
||||
self.handle_errors(partial(task.throw, Cancelled(task)), task)
|
||||
if task.state != TaskState.CANCELLED:
|
||||
task.pending_cancellation = True
|
||||
self.reschedule_running()
|
||||
self.paused.discard(task)
|
||||
self.io_release_task(task)
|
||||
self.run_ready.appendleft(task)
|
||||
self.handle_errors(partial(task.throw, Cancelled()), task)
|
||||
self.handle_errors(self.run_task_step)
|
||||
if task.state != TaskState.CANCELLED:
|
||||
task.pending_exception = Cancelled()
|
||||
|
||||
def throw(self, task, error):
|
||||
"""
|
||||
|
@ -682,7 +675,7 @@ class FIFOKernel:
|
|||
"""
|
||||
|
||||
task = Task(func.__name__ or repr(func), func(*args, **kwargs))
|
||||
# We inject our magic secret variable into the coroutine's stack frame so
|
||||
# We inject our magic secret variable into the coroutine's stack frame, so
|
||||
# we can look it up later
|
||||
task.coroutine.cr_frame.f_locals.setdefault(CTRLC_PROTECTION_ENABLED, False)
|
||||
task.scope = self.current_scope
|
||||
|
@ -736,7 +729,8 @@ class FIFOKernel:
|
|||
return
|
||||
elif self.current_task.last_io[1] == resource:
|
||||
# If the event to listen for has changed we just modify it
|
||||
self.selector.modify(resource, evt_type, {evt_type: self.current_task})
|
||||
key = self.selector.get_key(resource)
|
||||
self.selector.modify(resource, evt_type | key.events, key.data.update({evt_type: self.current_task}))
|
||||
self.current_task.last_io = (evt_type, resource)
|
||||
self.debugger.on_io_schedule(resource, evt_type)
|
||||
elif not self.current_task.last_io or self.current_task.last_io[1] != resource:
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import aiosched
|
||||
|
||||
|
||||
async def deadlock():
|
||||
await aiosched.Event().wait()
|
||||
|
||||
|
||||
async def parent():
|
||||
async with aiosched.skip_after(5):
|
||||
await deadlock()
|
||||
print("Done")
|
||||
|
||||
|
||||
aiosched.run(parent)
|
||||
print("Exited")
|
|
@ -32,4 +32,8 @@ async def main():
|
|||
print("Two-way proxy shutting down")
|
||||
|
||||
|
||||
aiosched.run(main)
|
||||
try:
|
||||
aiosched.run(main)
|
||||
except KeyboardInterrupt:
|
||||
print(f"Ctrl+C caught")
|
||||
|
||||
|
|
Reference in New Issue