@@ -303,29 +303,41 @@ def __init__(self):
303303 # Use a deque instead of Queue, as we don't require
304304 # synchronization between threads here.
305305 self .__callbacks = deque ()
306- # Set a 0-delay timer on itself, this will ensure that
307- # timerEvent gets fired each time after window events are processed
308- # See https://doc.qt.io/qt-6/qtimer.html#interval-prop
309- self .__timerId = self .startTimer (0 )
306+
307+ # Keep track of the current timer.
308+ # The queue can only have a single timer that services it.
309+ # Once fired, all pending callbacks will be processed.
310+ self .__timer_id = None
310311 self .__stopped = False
311312 self .__debug_enabled = False
312313
313314 def add_callback (self , handle ):
314315 # handle must be an asyncio.Handle
315316 self .__callbacks .append (handle )
316317 self .__log_debug ("Registering call_soon handle %s" , id (handle ))
318+
319+ # Create a timer if it doesn't yet exist
320+ if self .__timer_id is None :
321+ # Set a 0-delay timer on itself, this will ensure thats
322+ # it gets fired immediately after window events are processed the next time.
323+ # See https://doc.qt.io/qt-6/qtimer.html#interval-prop
324+ self .__timer_id = self .startTimer (0 )
325+ self .__log_debug ("Registering call_soon timer %s" , self .__timer_id )
317326 return handle
318327
319328 def timerEvent (self , event ):
320329 timerId = event .timerId ()
321- assert timerId == self .__timerId
330+ # We should have only one timer active at the same time, so
331+ # this assert will get hit only when something's very bad
332+ assert timerId == self .__timer_id
322333
323334 # Stop timer if stopped
324335 if self .__stopped :
325- self .killTimer (timerId )
326336 self .__log_debug ("call_soon queue stopped, clearing handles" )
327337 # TODO: Do we need to del the handles or somehow invalidate them?
328338 self .__callbacks .clear ()
339+ self .killTimer (timerId )
340+ self .__timer_id = None
329341 return
330342
331343 # Iterate over pending callbacks
@@ -335,6 +347,15 @@ def timerEvent(self, event):
335347 self .__log_debug ("Calling call_soon handle %s" , id (handle ))
336348 handle ._run ()
337349
350+ # No more callbacks exist, we can dispose this timer.
351+ # It will be recreated once a callback is registered again.
352+ # It's should be safe to assume that another thread isn't calling
353+ # add_callback during the lifetime of timerEvent
354+ self .__log_debug ("Stopping call_soon timer %s" , timerId )
355+ self .killTimer (timerId )
356+ self .__timer_id = None
357+ assert len (self .__callbacks ) == 0
358+
338359 def stop (self ):
339360 self .__log_debug ("Stopping call_soon queue" )
340361 self .__stopped = True
0 commit comments