openvidu-server: Session closingLock from .lock() to .tryLock()

pull/431/head
pabloFuente 2020-04-09 13:20:29 +02:00
parent cd1aad4eff
commit 7636d8a516
5 changed files with 145 additions and 74 deletions

View File

@ -28,6 +28,7 @@ import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -447,7 +448,8 @@ public abstract class SessionManager {
long sessionExistsSince = currentMillis - sessionNotActive.getStartTime(); long sessionExistsSince = currentMillis - sessionNotActive.getStartTime();
if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) { if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) {
try { try {
sessionNotActive.closingLock.writeLock().lock(); if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (sessions.containsKey(sessionId)) { if (sessions.containsKey(sessionId)) {
// The session passed to active during lock wait // The session passed to active during lock wait
continue; continue;
@ -458,6 +460,16 @@ public abstract class SessionManager {
} finally { } finally {
sessionNotActive.closingLock.writeLock().unlock(); sessionNotActive.closingLock.writeLock().unlock();
} }
} else {
log.error(
"Timeout waiting for Session closing lock to be available for garbage collector to clean session {}",
sessionId);
}
} catch (InterruptedException e) {
log.error(
"InterruptedException while waiting for Session closing lock to be available for garbage collector to clean session {}",
sessionId);
}
} }
} }
@ -515,7 +527,8 @@ public abstract class SessionManager {
// to the session. That is: if the session was in the automatic recording stop // to the session. That is: if the session was in the automatic recording stop
// timeout with INDIVIDUAL recording (no docker participant connected) // timeout with INDIVIDUAL recording (no docker participant connected)
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (session.isClosed()) { if (session.isClosed()) {
return; return;
} }
@ -523,6 +536,12 @@ public abstract class SessionManager {
} finally { } finally {
session.closingLock.writeLock().unlock(); session.closingLock.writeLock().unlock();
} }
} else {
log.error("Timeout waiting for Session {} closing lock to be available", sessionId);
}
} catch (InterruptedException e) {
log.error("InterruptedException while waiting for Session {} closing lock to be available", sessionId);
}
} }
} }

View File

@ -331,12 +331,18 @@ public class KurentoParticipant extends Participant {
} finally { } finally {
pub.closingLock.writeLock().unlock(); pub.closingLock.writeLock().unlock();
} }
} else {
log.error(
"Timeout waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
senderName, this.getParticipantPublicId());
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
subscribers.remove(senderName);
log.error( log.error(
"Timeout wating for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia", "InterruptedException while waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
senderName, this.getParticipantPublicId()); senderName, this.getParticipantPublicId());
} finally {
// Always clean map
subscribers.remove(senderName);
} }
} }
} }

View File

@ -86,7 +86,7 @@ public class KurentoSessionManager extends SessionManager {
@Override @Override
/* Protected by Session.closingLock.readLock */ /* Protected by Session.closingLock.readLock */
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) { public void joinRoom(Participant participant, String sessionId, Integer transactionId) {
Set<Participant> existingParticipants = null; Set<Participant> existingParticipants = null;
boolean lockAcquired = false; boolean lockAcquired = false;
try { try {
@ -162,8 +162,7 @@ public class KurentoSessionManager extends SessionManager {
} }
@Override @Override
public synchronized boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason, public boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason, boolean closeWebSocket) {
boolean closeWebSocket) {
log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}", log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}",
participant.getParticipantPublicId(), participant.getSessionId(), participant.getParticipantPublicId(), participant.getSessionId(),
reason != null ? reason.name() : "NULL"); reason != null ? reason.name() : "NULL");
@ -235,7 +234,8 @@ public class KurentoSessionManager extends SessionManager {
recordingManager.initAutomaticRecordingStopThread(session); recordingManager.initAutomaticRecordingStopThread(session);
} else { } else {
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (session.isClosed()) { if (session.isClosed()) {
return false; return false;
} }
@ -245,7 +245,16 @@ public class KurentoSessionManager extends SessionManager {
} finally { } finally {
session.closingLock.writeLock().unlock(); session.closingLock.writeLock().unlock();
} }
} else {
log.error(
"Timeout waiting for Session {} closing lock to be available for closing as last participant left",
sessionId);
}
} catch (InterruptedException e) {
log.error(
"InterruptedException while waiting for Session {} closing lock to be available for closing as last participant left",
sessionId);
}
} }
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled() } else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode()) && MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())

View File

@ -480,23 +480,24 @@ public class RecordingManager {
recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout()); recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout());
if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) { if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) {
boolean alreadyUnlocked = false; boolean alreadyUnlocked = false;
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (session.isClosed()) { if (session.isClosed()) {
return; return;
} }
if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) { if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) {
// Close session if there are no participants connected (RECORDER does not // Close session if there are no participants connected (RECORDER does not
// count) and publishing // count) and publishing
log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(), log.info("Closing session {} after automatic stop of recording {}",
recordingId); session.getSessionId(), recordingId);
sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop, true); sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop,
true);
} else { } else {
// There are users connected, but no one is publishing // There are users connected, but no one is publishing
session.closingLock.writeLock().unlock(); // We don't need the lock if session's not closing // We don't need the lock if session is not closing
session.closingLock.writeLock().unlock();
alreadyUnlocked = true; alreadyUnlocked = true;
log.info( log.info(
"Automatic stopping recording {}. There are users connected to session {}, but no one is publishing", "Automatic stopping recording {}. There are users connected to session {}, but no one is publishing",
@ -508,6 +509,17 @@ public class RecordingManager {
session.closingLock.writeLock().unlock(); session.closingLock.writeLock().unlock();
} }
} }
} else {
log.error(
"Timeout waiting for Session {} closing lock to be available for automatic recording stop thred",
session.getSessionId());
}
} catch (InterruptedException e) {
log.error(
"InterruptedException while waiting for Session {} closing lock to be available for automatic recording stop thred",
session.getSessionId());
}
} else { } else {
// This code shouldn't be reachable // This code shouldn't be reachable
log.warn("Recording {} was already automatically stopped by a previous thread", recordingId); log.warn("Recording {} was already automatically stopped by a previous thread", recordingId);
@ -523,7 +535,8 @@ public class RecordingManager {
if (future != null) { if (future != null) {
boolean cancelled = future.cancel(false); boolean cancelled = future.cancel(false);
try { try {
session.closingLock.writeLock().lock(); if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (session.isClosed()) { if (session.isClosed()) {
return false; return false;
} }
@ -536,10 +549,20 @@ public class RecordingManager {
session.getSessionId()); session.getSessionId());
sessionManager.closeSessionAndEmptyCollections(session, reason, false); sessionManager.closeSessionAndEmptyCollections(session, reason, false);
} }
return cancelled;
} finally { } finally {
session.closingLock.writeLock().unlock(); session.closingLock.writeLock().unlock();
} }
} else {
log.error(
"Timeout waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
session.getSessionId());
}
} catch (InterruptedException e) {
log.error(
"InterruptedException while waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
session.getSessionId());
}
return cancelled;
} else { } else {
return true; return true;
} }

View File

@ -21,6 +21,7 @@ import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
@ -234,16 +235,29 @@ public class SessionRestController {
Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId); Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId);
if (sessionNotActive != null) { if (sessionNotActive != null) {
try { try {
sessionNotActive.closingLock.writeLock().lock(); if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
try {
if (sessionNotActive.isClosed()) { if (sessionNotActive.isClosed()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} }
this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive, EndReason.sessionClosedByServer, this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive,
true); EndReason.sessionClosedByServer, true);
return new ResponseEntity<>(HttpStatus.NO_CONTENT); return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} finally { } finally {
sessionNotActive.closingLock.writeLock().unlock(); sessionNotActive.closingLock.writeLock().unlock();
} }
} else {
String errorMsg = "Timeout waiting for Session " + sessionId
+ " closing lock to be available for closing from DELETE /api/sessions";
log.error(errorMsg);
return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
}
} catch (InterruptedException e) {
String errorMsg = "InterruptedException while waiting for Session " + sessionId
+ " closing lock to be available for closing from DELETE /api/sessions";
log.error(errorMsg);
return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
}
} else { } else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} }