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():
|
if task.done():
|
||||||
return
|
return
|
||||||
|
if task.scope and not task.scope.cancellable:
|
||||||
|
return
|
||||||
await syscall("cancel", task)
|
await syscall("cancel", task)
|
||||||
if block:
|
if block:
|
||||||
await wait(task)
|
await wait(task)
|
||||||
|
|
|
@ -126,10 +126,12 @@ class FIFOKernel:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._sigint_handled = True
|
|
||||||
if not currently_protected():
|
if not currently_protected():
|
||||||
self.run_ready.appendleft(self.entry_point)
|
self.run_ready.appendleft(self.entry_point)
|
||||||
|
self.entry_point.pending_exception = KeyboardInterrupt()
|
||||||
self.handle_errors(self.run_task_step)
|
self.handle_errors(self.run_task_step)
|
||||||
|
else:
|
||||||
|
self._sigint_handled = True
|
||||||
|
|
||||||
def done(self) -> bool:
|
def done(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -352,28 +354,17 @@ class FIFOKernel:
|
||||||
# there are no more runnable tasks
|
# there are no more runnable tasks
|
||||||
return
|
return
|
||||||
self.current_task = self.run_ready.popleft()
|
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
|
self._running = True
|
||||||
_runner = self.current_task.run
|
_runner = self.current_task.run
|
||||||
_data = [self.data.pop(self.current_task, None)]
|
_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
|
# Some debugging and internal chatter here
|
||||||
self.current_task.steps += 1
|
self.current_task.steps += 1
|
||||||
self.current_task.state = TaskState.RUN
|
self.current_task.state = TaskState.RUN
|
||||||
self.debugger.before_task_step(self.current_task)
|
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
|
# Run a single step with the calculation (i.e. until a yield
|
||||||
# somewhere)
|
# somewhere)
|
||||||
method, args, kwargs = _runner(*_data)
|
method, args, kwargs = _runner(*_data)
|
||||||
|
@ -456,7 +447,9 @@ class FIFOKernel:
|
||||||
else:
|
else:
|
||||||
task = self.current_task
|
task = self.current_task
|
||||||
"""
|
"""
|
||||||
|
self._sigint_handled = False
|
||||||
task = self.entry_point
|
task = self.entry_point
|
||||||
|
task.pending_exception = KeyboardInterrupt()
|
||||||
self.run_ready.appendleft(task)
|
self.run_ready.appendleft(task)
|
||||||
self.handle_errors(self.run_task_step)
|
self.handle_errors(self.run_task_step)
|
||||||
elif not self.run_ready:
|
elif not self.run_ready:
|
||||||
|
@ -579,14 +572,14 @@ class FIFOKernel:
|
||||||
it fails
|
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.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):
|
def throw(self, task, error):
|
||||||
"""
|
"""
|
||||||
|
@ -682,7 +675,7 @@ class FIFOKernel:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
task = Task(func.__name__ or repr(func), func(*args, **kwargs))
|
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
|
# we can look it up later
|
||||||
task.coroutine.cr_frame.f_locals.setdefault(CTRLC_PROTECTION_ENABLED, False)
|
task.coroutine.cr_frame.f_locals.setdefault(CTRLC_PROTECTION_ENABLED, False)
|
||||||
task.scope = self.current_scope
|
task.scope = self.current_scope
|
||||||
|
@ -736,7 +729,8 @@ class FIFOKernel:
|
||||||
return
|
return
|
||||||
elif self.current_task.last_io[1] == resource:
|
elif self.current_task.last_io[1] == resource:
|
||||||
# If the event to listen for has changed we just modify it
|
# 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.current_task.last_io = (evt_type, resource)
|
||||||
self.debugger.on_io_schedule(resource, evt_type)
|
self.debugger.on_io_schedule(resource, evt_type)
|
||||||
elif not self.current_task.last_io or self.current_task.last_io[1] != resource:
|
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")
|
print("Two-way proxy shutting down")
|
||||||
|
|
||||||
|
|
||||||
aiosched.run(main)
|
try:
|
||||||
|
aiosched.run(main)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print(f"Ctrl+C caught")
|
||||||
|
|
||||||
|
|
Reference in New Issue