Skip to content

Commit

Permalink
Merge pull request #13004 from nextcloud/backport/12969/stable30
Browse files Browse the repository at this point in the history
[stable30] fix(call): Fix race condition of "End call for everyone" and a parall…
  • Loading branch information
nickvergessen authored Aug 15, 2024
2 parents 8fc491e + cc6a7a3 commit dbc2e83
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 16 deletions.
8 changes: 7 additions & 1 deletion lib/Controller/CallController.php
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,14 @@ public function leaveCall(bool $all = false): DataResponse {
}

if ($all && $this->participant->hasModeratorPermissions()) {
$result = $this->roomService->resetActiveSinceInDatabaseOnly($this->room);
if (!$result) {
// Someone else won the race condition, make sure this user disconnects directly and then return
$this->participantService->changeInCall($this->room, $this->participant, Participant::FLAG_DISCONNECTED);
return new DataResponse();
}
$this->participantService->endCallForEveryone($this->room, $this->participant);
$this->roomService->resetActiveSince($this->room, $this->participant, true);
$this->roomService->resetActiveSinceInModelOnly($this->room);
} else {
$this->participantService->changeInCall($this->room, $this->participant, Participant::FLAG_DISCONNECTED);
if (!$this->participantService->hasActiveSessionsInCall($this->room)) {
Expand Down
40 changes: 25 additions & 15 deletions lib/Service/RoomService.php
Original file line number Diff line number Diff line change
Expand Up @@ -836,19 +836,12 @@ public function setBreakoutRoomStatus(Room $room, int $status): bool {
return true;
}

public function resetActiveSince(Room $room, ?Participant $participant, bool $alreadyTriggeredCallEndedForEveryone = false): void {
$oldActiveSince = $room->getActiveSince();
$oldCallFlag = $room->getCallFlag();

if (!$alreadyTriggeredCallEndedForEveryone) {
if ($oldActiveSince === null && $oldCallFlag === Participant::FLAG_DISCONNECTED) {
return;
}

$event = new BeforeCallEndedEvent($room, $participant, $oldActiveSince);
$this->dispatcher->dispatchTyped($event);
}

/**
* @internal Warning! Use with care, this is only used to make sure we win the race condition for posting the final messages
* when "End call for everyone" is used where we print the chat messages before testing the race condition,
* so that no other participant leaving would trigger a call summary
*/
public function resetActiveSinceInDatabaseOnly(Room $room): bool {
$update = $this->db->getQueryBuilder();
$update->update('talk_rooms')
->set('active_since', $update->createNamedParameter(null, IQueryBuilder::PARAM_DATE))
Expand All @@ -857,15 +850,32 @@ public function resetActiveSince(Room $room, ?Participant $participant, bool $al
->where($update->expr()->eq('id', $update->createNamedParameter($room->getId(), IQueryBuilder::PARAM_INT)))
->andWhere($update->expr()->isNotNull('active_since'));

$result = (bool) $update->executeStatement();
return (bool) $update->executeStatement();
}

/**
* @internal Warning! Must only be used after {@see preResetActiveSinceInDatabaseOnly()}
* was called and returned `true`
*/
public function resetActiveSinceInModelOnly(Room $room): void {
$room->resetActiveSince();
$room->setCallPermissions(Attendee::PERMISSIONS_DEFAULT);
}

public function resetActiveSince(Room $room, ?Participant $participant): void {
$oldActiveSince = $room->getActiveSince();
$oldCallFlag = $room->getCallFlag();

if ($alreadyTriggeredCallEndedForEveryone) {
if ($oldActiveSince === null && $oldCallFlag === Participant::FLAG_DISCONNECTED) {
return;
}

$event = new BeforeCallEndedEvent($room, $participant, $oldActiveSince);
$this->dispatcher->dispatchTyped($event);

$result = $this->resetActiveSinceInDatabaseOnly($room);
$this->resetActiveSinceInModelOnly($room);

if (!$result) {
// Lost the race, someone else updated the database
return;
Expand Down

0 comments on commit dbc2e83

Please sign in to comment.