Skip to content

Commit c8d4273

Browse files
committed
clean up memory leak
1 parent dfe87f0 commit c8d4273

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

packages/js/src/fabric/WSClient.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { PREVIOUS_CALLID_STORAGE_KEY } from './utils/constants'
3232
export class WSClient extends BaseClient<{}> implements WSClientContract {
3333
private _incomingCallManager: IncomingCallManager
3434
private _disconnected: boolean = false
35+
private _eventCleanup: Array<() => void> = []
3536

3637
constructor(private wsClientOptions: WSClientOptions) {
3738
const client = createWSClient(wsClientOptions)
@@ -305,10 +306,24 @@ export class WSClient extends BaseClient<{}> implements WSClientContract {
305306
this._disconnected = true
306307
})
307308

309+
// Clean up all event listeners
310+
this._cleanupEventListeners()
311+
308312
super.disconnect()
309313
})
310314
}
311315

316+
private _cleanupEventListeners() {
317+
this._eventCleanup.forEach(cleanup => {
318+
try {
319+
cleanup()
320+
} catch (error) {
321+
this.logger.error('Error cleaning up event listener:', error)
322+
}
323+
})
324+
this._eventCleanup = []
325+
}
326+
312327
public async dial(params: DialParams) {
313328
return new Promise<FabricRoomSession>(async (resolve, reject) => {
314329
try {

packages/webrtc/src/RTCPeer.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
4040
private _processingLocalSDP = false
4141
private _waitNegotiation: Promise<void> = Promise.resolve()
4242
private _waitNegotiationCompleter: () => void
43+
private _eventCleanup: Array<() => void> = []
4344
/**
4445
* Both of these properties are used to have granular
4546
* control over when to `resolve` and when `reject` the
@@ -466,7 +467,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
466467
}
467468

468469
this.instance.removeEventListener('icecandidate', this._onIce)
469-
this.instance.addEventListener('icecandidate', this._onIce)
470+
this._addEventListener(this.instance, 'icecandidate', this._onIce as EventListener)
470471
if (this.isOffer) {
471472
this.logger.debug('Trying to generate offer')
472473
const offerOptions: RTCOfferOptions = {
@@ -876,6 +877,16 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
876877
this.clearResumeTimer()
877878
this.clearConnectionStateTimer()
878879

880+
// Clean up all event listeners
881+
this._eventCleanup.forEach(cleanup => {
882+
try {
883+
cleanup()
884+
} catch (error) {
885+
this.logger.error('Error cleaning up event listener:', error)
886+
}
887+
})
888+
this._eventCleanup = []
889+
879890
// Do not use `stopTrack` util to not dispatch the `ended` event
880891
this._localStream?.getTracks().forEach((track) => track.stop())
881892
this._remoteStream?.getTracks().forEach((track) => track.stop())
@@ -1200,8 +1211,15 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
12001211
return getUserMedia(constraints)
12011212
}
12021213

1214+
private _addEventListener(target: EventTarget, event: string, handler: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) {
1215+
target.addEventListener(event, handler, options)
1216+
this._eventCleanup.push(() => {
1217+
target.removeEventListener(event, handler, options)
1218+
})
1219+
}
1220+
12031221
private _attachListeners() {
1204-
this.instance.addEventListener('signalingstatechange', () => {
1222+
this._addEventListener(this.instance, 'signalingstatechange', () => {
12051223
this.logger.debug('signalingState:', this.instance.signalingState)
12061224

12071225
switch (this.instance.signalingState) {
@@ -1246,7 +1264,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
12461264
}
12471265
})
12481266

1249-
this.instance.addEventListener('connectionstatechange', () => {
1267+
this._addEventListener(this.instance, 'connectionstatechange', () => {
12501268
this.logger.debug('connectionState:', this.instance.connectionState)
12511269
switch (this.instance.connectionState) {
12521270
// case 'new':
@@ -1303,7 +1321,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
13031321
}
13041322
})
13051323

1306-
this.instance.addEventListener('negotiationneeded', () => {
1324+
this._addEventListener(this.instance, 'negotiationneeded', () => {
13071325
this.logger.debug('Negotiation needed event, signaling state:', this.instance.signalingState)
13081326

13091327
// For answer types (incoming calls), only negotiate if we're in specific states
@@ -1322,11 +1340,11 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
13221340
}
13231341
})
13241342

1325-
this.instance.addEventListener('iceconnectionstatechange', () => {
1343+
this._addEventListener(this.instance, 'iceconnectionstatechange', () => {
13261344
this.logger.debug('iceConnectionState:', this.instance.iceConnectionState)
13271345
})
13281346

1329-
this.instance.addEventListener('icegatheringstatechange', () => {
1347+
this._addEventListener(this.instance, 'icegatheringstatechange', () => {
13301348
this.logger.debug('iceGatheringState:', this.instance.iceGatheringState)
13311349
if (this.instance.iceGatheringState === 'complete') {
13321350
this.logger.debug('ICE gathering complete')
@@ -1338,7 +1356,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
13381356
// this.logger.warn('IceCandidate Error:', event)
13391357
// })
13401358

1341-
this.instance.addEventListener('track', (event: RTCTrackEvent) => {
1359+
this._addEventListener(this.instance, 'track', ((event: RTCTrackEvent) => {
13421360
this.logger.debug('Track event:', event, event.track.kind)
13431361
// @ts-expect-error
13441362
this.call.emit('track', event)
@@ -1348,10 +1366,10 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
13481366
// this.call._dispatchNotification(notification)
13491367
}
13501368
this._remoteStream = event.streams[0]
1351-
})
1369+
}) as EventListener)
13521370

13531371
// @ts-ignore
1354-
this.instance.addEventListener('addstream', (event: MediaStreamEvent) => {
1372+
this._addEventListener(this.instance, 'addstream', (event: MediaStreamEvent) => {
13551373
if (event.stream) {
13561374
this._remoteStream = event.stream
13571375
}
@@ -1396,13 +1414,13 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
13961414

13971415
public _attachAudioTrackListener() {
13981416
this.localStream?.getAudioTracks().forEach((track) => {
1399-
track.addEventListener('ended', this._onEndedTrackHandler)
1417+
this._addEventListener(track, 'ended', this._onEndedTrackHandler)
14001418
})
14011419
}
14021420

14031421
public _attachVideoTrackListener() {
14041422
this.localStream?.getVideoTracks().forEach((track) => {
1405-
track.addEventListener('ended', this._onEndedTrackHandler)
1423+
this._addEventListener(track, 'ended', this._onEndedTrackHandler)
14061424
})
14071425
}
14081426

0 commit comments

Comments
 (0)