Fix race condition when Sendspin player reconnect (#2772)
authorMaxim Raznatovski <nda.mr43@gmail.com>
Mon, 8 Dec 2025 23:11:24 +0000 (00:11 +0100)
committerGitHub <noreply@github.com>
Mon, 8 Dec 2025 23:11:24 +0000 (00:11 +0100)
music_assistant/providers/sendspin/player.py
music_assistant/providers/sendspin/provider.py

index f889cda7a9c89496c44b18c9512f7b9cf87e2668..8c42ccff9436936fd656fd1f85e2f482f19d44b8 100644 (file)
@@ -597,4 +597,3 @@ class SendspinPlayer(Player):
         await super().on_unload()
         self.unsub_event_cb()
         self.unsub_group_event_cb()
-        await self.api.disconnect()
index 2f1ae4c59cbae662a5215f21dbdbdf345a308d57..66437afb1e86c436327b4a3d413bf6b23a7f07bd 100644 (file)
@@ -43,6 +43,7 @@ class SendspinProvider(PlayerProvider):
     server_api: SendspinServer
     unregister_cbs: list[Callable[[], None]]
     _webrtc_sessions: dict[str, SendspinWebRTCSession]
+    _pending_unregisters: dict[str, asyncio.Event]
 
     def __init__(
         self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
@@ -53,6 +54,7 @@ class SendspinProvider(PlayerProvider):
             self.mass.loop, mass.server_id, "Music Assistant", self.mass.http_session
         )
         self._webrtc_sessions = {}
+        self._pending_unregisters = {}
         self.unregister_cbs = [
             self.server_api.add_event_listener(self.event_cb),
             # WebRTC signaling commands for Sendspin connections
@@ -72,12 +74,26 @@ class SendspinProvider(PlayerProvider):
         self.logger.debug("Received SendspinEvent: %s", event)
         match event:
             case ClientAddedEvent(client_id):
+                # Wait for any pending unregister to complete before registering
+                # This prevents a race condition where a slow unregister removes
+                # a newly registered player after a quick reconnect
+                if pending_event := self._pending_unregisters.get(client_id):
+                    self.logger.debug(
+                        "Waiting for pending unregister of %s before registering", client_id
+                    )
+                    await pending_event.wait()
                 player = SendspinPlayer(self, client_id)
                 self.logger.debug("Client %s connected", client_id)
                 await self.mass.players.register(player)
             case ClientRemovedEvent(client_id):
                 self.logger.debug("Client %s disconnected", client_id)
-                await self.mass.players.unregister(client_id)
+                unregister_event = asyncio.Event()
+                self._pending_unregisters[client_id] = unregister_event
+                try:
+                    await self.mass.players.unregister(client_id)
+                finally:
+                    self._pending_unregisters.pop(client_id, None)
+                    unregister_event.set()
             case _:
                 self.logger.error("Unknown sendspin event: %s", event)