Browse Source

fix(call): Fix race condition of "End call for everyone" and a parallel "Leave call"

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/12969/head
Joas Schilling 1 year ago
parent
commit
4d73f9bdb6
No known key found for this signature in database GPG Key ID: 74434EFE0D2E2205
  1. 8
      lib/Controller/CallController.php
  2. 40
      lib/Service/RoomService.php

8
lib/Controller/CallController.php

@ -423,8 +423,14 @@ class CallController extends AEnvironmentAwareController {
}
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)) {

40
lib/Service/RoomService.php

@ -836,19 +836,12 @@ class RoomService {
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))
@ -857,15 +850,32 @@ class RoomService {
->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;

Loading…
Cancel
Save