diff options
author | 2020-02-25 22:22:01 -0800 | |
---|---|---|
committer | 2020-02-25 22:25:20 -0800 | |
commit | 4a11bf22cc9e894271b896eb9fca0c3cff085766 (patch) | |
tree | ff9047367d728f3dd3d91f5589b8b108ebb033c4 | |
parent | Scheduler: improve cancel_task's docstring (diff) |
Scheduler: only delete the task in the done callback if tasks are same
To prevent a deletion of task rescheduled with the same ID, the callback
checks that the stored task is the same as the done task being handled.
* Only delete the task; it doesn't need to be cancelled because the it
is already done
* Revise the callback's docstring to explain the new behaviour
* Rename `task` parameter to `done_task`
-rw-r--r-- | bot/utils/scheduling.py | 41 |
1 files changed, 25 insertions, 16 deletions
diff --git a/bot/utils/scheduling.py b/bot/utils/scheduling.py index cb28648db..58bb32e5d 100644 --- a/bot/utils/scheduling.py +++ b/bot/utils/scheduling.py @@ -65,24 +65,33 @@ class Scheduler(metaclass=CogABCMeta): log.debug(f"{self.cog_name}: unscheduled task #{task_id} {id(task)}.") - def _task_done_callback(self, task_id: str, task: asyncio.Task) -> None: + def _task_done_callback(self, task_id: str, done_task: asyncio.Task) -> None: """ - Unschedule the task and raise its exception if one exists. + Delete the task and raise its exception if one exists. - If the task was cancelled, the CancelledError is retrieved and suppressed. In this case, - the task is already assumed to have been unscheduled. + If `done_task` and the task associated with `task_id` are different, then the latter + will not be deleted. In this case, a new task was likely rescheduled with the same ID. """ - log.trace(f"{self.cog_name}: performing done callback for task #{task_id} {id(task)}") + log.trace(f"{self.cog_name}: performing done callback for task #{task_id} {id(done_task)}.") - if task.cancelled(): - with contextlib.suppress(asyncio.CancelledError): - task.exception() + scheduled_task = self._scheduled_tasks.get(task_id) + + if scheduled_task and done_task is scheduled_task: + # A task for the ID exists and its the same as the done task. + # Since this is the done callback, the task is already done so no need to cancel it. + log.trace(f"{self.cog_name}: deleting task #{task_id} {id(done_task)}.") + del self._scheduled_tasks[task_id] + elif scheduled_task: + # A new task was likely rescheduled with the same ID. + log.debug( + f"{self.cog_name}: the scheduled task #{task_id} {id(scheduled_task)} " + f"and the done task {id(done_task)} differ." + ) else: - # Check if it exists to avoid logging a warning. - if task_id in self._scheduled_tasks: - # Only cancel if the task is not cancelled to avoid a race condition when a new - # task is scheduled using the same ID. Reminders do this when re-scheduling after - # editing. - self.cancel_task(task_id) - - task.exception() + log.warning( + f"{self.cog_name}: task #{task_id} not found while handling task {id(done_task)}! " + f"A task somehow got unscheduled improperly (i.e. deleted but not cancelled)." + ) + + with contextlib.suppress(asyncio.CancelledError): + done_task.exception() |