File tree Expand file tree Collapse file tree 2 files changed +44
-0
lines changed
Expand file tree Collapse file tree 2 files changed +44
-0
lines changed Original file line number Diff line number Diff line change @@ -333,7 +333,17 @@ def clear(self) -> None:
333333 Clear all members and reset sync state.
334334
335335 Used when channel enters DETACHED or FAILED state (RTP5a).
336+ Invokes any pending sync callbacks before clearing to ensure
337+ waiting Futures are resolved and callers are not left blocked.
336338 """
339+ # Notify any callbacks waiting for sync to complete
340+ # This ensures Futures created by _wait_for_sync() are resolved
341+ for callback in self ._sync_complete_callbacks :
342+ try :
343+ callback ()
344+ except Exception as e :
345+ self ._logger .error (f"Error in sync complete callback during clear: { e } " )
346+
337347 self ._map .clear ()
338348 self ._residual_members = None
339349 self ._sync_in_progress = False
Original file line number Diff line number Diff line change @@ -736,3 +736,37 @@ def test_start_sync_multiple_times(self):
736736 # Call start_sync again - should not reset residual
737737 self .presence_map .start_sync ()
738738 assert self .presence_map ._residual_members is initial_residual
739+
740+ def test_clear_invokes_sync_callbacks (self ):
741+ """
742+ Test that clear() invokes pending sync callbacks to prevent hanging.
743+
744+ This ensures that if get() is waiting for sync and the channel
745+ transitions to DETACHED/FAILED, the waiting Future is resolved
746+ and the caller is not left blocked.
747+ """
748+ msg1 = PresenceMessage (
749+ id = 'conn1:0:0' ,
750+ connection_id = 'conn1' ,
751+ client_id = 'client1' ,
752+ action = PresenceAction .PRESENT
753+ )
754+
755+ self .presence_map .put (msg1 )
756+ self .presence_map .start_sync ()
757+
758+ # Register a callback as if _wait_for_sync() was called
759+ callback_invoked = False
760+
761+ def sync_callback ():
762+ nonlocal callback_invoked
763+ callback_invoked = True
764+
765+ self .presence_map .wait_sync (sync_callback )
766+
767+ # Clear should invoke the callback
768+ self .presence_map .clear ()
769+
770+ assert callback_invoked , "clear() should invoke pending sync callbacks"
771+ assert not self .presence_map .sync_in_progress
772+ assert len (self .presence_map .values ()) == 0
You can’t perform that action at this time.
0 commit comments