From b9cccf91098b14954c7b92b893b1be848186056f Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 31 Oct 2022 12:08:19 -0400 Subject: [PATCH] Resolve races between initLocalCallFeed and leave Unfortunately there are still other methods that could race with leave and result in broken group call state, such as enter and terminate. For the future, should consider writing a more careful specification of how the whole group call state machine is meant to work. --- spec/unit/webrtc/groupCall.spec.ts | 9 ++++++++- src/webrtc/groupCall.ts | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/spec/unit/webrtc/groupCall.spec.ts b/spec/unit/webrtc/groupCall.spec.ts index e9a9c8a93ea..f83de56e5c2 100644 --- a/spec/unit/webrtc/groupCall.spec.ts +++ b/spec/unit/webrtc/groupCall.spec.ts @@ -23,7 +23,7 @@ import { Room, RoomMember, } from '../../../src'; -import { GroupCall, GroupCallEvent } from "../../../src/webrtc/groupCall"; +import { GroupCall, GroupCallEvent, GroupCallState } from "../../../src/webrtc/groupCall"; import { MatrixClient } from "../../../src/client"; import { installWebRTCMocks, @@ -174,6 +174,13 @@ describe('Group Call', function() { groupCall.leave(); }); + it("stops initializing local call feed when leaving", async () => { + const initPromise = groupCall.initLocalCallFeed(); + groupCall.leave(); + await expect(initPromise).rejects.toBeDefined(); + expect(groupCall.state).toBe(GroupCallState.LocalCallFeedUninitialized); + }); + it("sends state event to room when creating", async () => { await groupCall.create(); diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index 5b3bb1ee056..cb73b7183e3 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -257,13 +257,26 @@ export class GroupCall extends TypedEventEmitter< let stream: MediaStream; + let disposed = false; + const onState = (state: GroupCallState) => { + if (state === GroupCallState.LocalCallFeedUninitialized) { + disposed = true; + } + }; + this.on(GroupCallEvent.GroupCallStateChanged, onState); + try { stream = await this.client.getMediaHandler().getUserMediaStream(true, this.type === GroupCallType.Video); } catch (error) { this.setState(GroupCallState.LocalCallFeedUninitialized); throw error; + } finally { + this.off(GroupCallEvent.GroupCallStateChanged, onState); } + // The call could've been disposed while we were waiting + if (disposed) throw new Error("Group call disposed"); + const userId = this.client.getUserId()!; const callFeed = new CallFeed({