From aa59b32031ce72dcfd8c8e96dc933f2ad37b16ed Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 3 Sep 2025 13:45:13 -0400 Subject: [PATCH] reactor: Prevent update_timer() from running a single timer multiple times The "lazy" greenlet implementation could allow the same timer to run multiple times in parallel if the first timer instance calls pause() and another task calls update_timer(). This is confusing and can cause hard to debug errors. Add a new timer_is_running flag to prevent it. Signed-off-by: Kevin O'Connor --- klippy/reactor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/klippy/reactor.py b/klippy/reactor.py index 412d53edf..f9bedcf3f 100644 --- a/klippy/reactor.py +++ b/klippy/reactor.py @@ -1,6 +1,6 @@ # File descriptor and timer event helper # -# Copyright (C) 2016-2020 Kevin O'Connor +# Copyright (C) 2016-2025 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import os, gc, select, math, time, logging, queue @@ -14,6 +14,7 @@ class ReactorTimer: def __init__(self, callback, waketime): self.callback = callback self.waketime = waketime + self.timer_is_running = False class ReactorCompletion: class sentinel: pass @@ -118,6 +119,8 @@ class SelectReactor: return tuple(self._last_gc_times) # Timers def update_timer(self, timer_handler, waketime): + if timer_handler.timer_is_running: + return timer_handler.waketime = waketime self._next_timer = min(self._next_timer, waketime) def register_timer(self, callback, waketime=NEVER): @@ -155,7 +158,9 @@ class SelectReactor: waketime = t.waketime if eventtime >= waketime: t.waketime = self.NEVER + t.timer_is_running = True t.waketime = waketime = t.callback(eventtime) + t.timer_is_running = False if g_dispatch is not self._g_dispatch: self._next_timer = min(self._next_timer, waketime) self._end_greenlet(g_dispatch)