@@ -331,6 +331,17 @@ export class WebRTCSignalingService {
331331 return ;
332332 }
333333
334+ // Check and close existing connection first to prevent memory leaks
335+ const existingConnection = this . peerConnections . get ( targetUser ) ;
336+ if ( existingConnection ) {
337+ this . logger . warn (
338+ 'createPeerConnection' ,
339+ `Peer connection already exists for ${ targetUser } , closing old one`
340+ ) ;
341+ existingConnection . close ( ) ;
342+ this . peerConnections . delete ( targetUser ) ;
343+ }
344+
334345 const peerConnection = new RTCPeerConnection ( RTC_CONFIGURATION ) ;
335346
336347 let iceGatheringTimeout : ReturnType < typeof setTimeout > | null = null ;
@@ -635,10 +646,14 @@ export class WebRTCSignalingService {
635646 }
636647 }
637648
649+ // Set lock while processing offer to prevent concurrent connection attempts
650+ this . connectionLocks . add ( targetUser ) ;
651+
638652 const peerConnection = this . createPeerConnection ( targetUser ) ;
639653
640654 if ( ! peerConnection ) {
641655 this . logger . error ( 'handleOffer' , `PeerConnection missing for ${ targetUser } ` ) ;
656+ this . connectionLocks . delete ( targetUser ) ;
642657 return ;
643658 }
644659
@@ -651,6 +666,8 @@ export class WebRTCSignalingService {
651666 return peerConnection . setLocalDescription ( answer ) ;
652667 } )
653668 . then ( ( ) => {
669+ // Release lock after answer is sent
670+ this . connectionLocks . delete ( targetUser ) ;
654671 const response : SignalMessage = {
655672 type : SignalMessageType . ANSWER ,
656673 data : peerConnection . localDescription ,
@@ -661,6 +678,8 @@ export class WebRTCSignalingService {
661678 this . processCandidateQueue ( targetUser ) ;
662679 } )
663680 . catch ( ( error ) => {
681+ // Release lock on error
682+ this . connectionLocks . delete ( targetUser ) ;
664683 this . logger . error ( 'handleOffer' , `Error handling offer: ${ error } ` ) ;
665684 if ( this . wsService . isConnected ( ) ) {
666685 this . toaster . warning (
@@ -741,6 +760,17 @@ export class WebRTCSignalingService {
741760 clearTimeout ( existingTimeout ) ;
742761 }
743762
763+ // Clear reconnection timeout to prevent collision with state mismatch recovery
764+ const reconnectionTimeout = this . reconnectionTimeouts . get ( targetUser ) ;
765+ if ( reconnectionTimeout ) {
766+ clearTimeout ( reconnectionTimeout ) ;
767+ this . reconnectionTimeouts . delete ( targetUser ) ;
768+ this . logger . debug (
769+ 'handleStateMismatch' ,
770+ `Cleared reconnection timeout for ${ targetUser } to prevent collision`
771+ ) ;
772+ }
773+
744774 const timeoutId = setTimeout ( ( ) => {
745775 this . stateMismatchTimeouts . delete ( targetUser ) ;
746776 this . initiateConnection ( targetUser ) ;
0 commit comments