@ -21,6 +21,7 @@ namespace Emby.Server.Implementations.Session
private readonly SessionInfo _session ;
private readonly List < IWebSocketConnection > _sockets ;
private readonly ReaderWriterLockSlim _socketsLock ;
private bool _disposed = false ;
public WebSocketController (
@ -31,10 +32,26 @@ namespace Emby.Server.Implementations.Session
_logger = logger ;
_session = session ;
_sessionManager = sessionManager ;
_sockets = new List < IWebSocketConnection > ( ) ;
_sockets = new ( ) ;
_socketsLock = new ( ) ;
}
private bool HasOpenSockets = > GetActiveSockets ( ) . Any ( ) ;
private bool HasOpenSockets
{
get
{
ObjectDisposedException . ThrowIf ( _disposed , this ) ;
try
{
_socketsLock . EnterReadLock ( ) ;
return _sockets . Any ( i = > i . State = = WebSocketState . Open ) ;
}
finally
{
_socketsLock . ExitReadLock ( ) ;
}
}
}
/// <inheritdoc />
public bool SupportsMediaControl = > HasOpenSockets ;
@ -42,23 +59,38 @@ namespace Emby.Server.Implementations.Session
/// <inheritdoc />
public bool IsSessionActive = > HasOpenSockets ;
private IEnumerable < IWebSocketConnection > GetActiveSockets ( )
= > _sockets . Where ( i = > i . State = = WebSocketState . Open ) ;
public void AddWebSocket ( IWebSocketConnection connection )
{
_logger . LogDebug ( "Adding websocket to session {Session}" , _session . Id ) ;
_sockets . Add ( connection ) ;
connection . Closed + = OnConnectionClosed ;
ObjectDisposedException . ThrowIf ( _disposed , this ) ;
try
{
_socketsLock . EnterWriteLock ( ) ;
_sockets . Add ( connection ) ;
connection . Closed + = OnConnectionClosed ;
}
finally
{
_socketsLock . ExitWriteLock ( ) ;
}
}
private async void OnConnectionClosed ( object? sender , EventArgs e )
{
var connection = sender as IWebSocketConnection ? ? throw new ArgumentException ( $"{nameof(sender)} is not of type {nameof(IWebSocketConnection)}" , nameof ( sender ) ) ;
_logger . LogDebug ( "Removing websocket from session {Session}" , _session . Id ) ;
_sockets . Remove ( connection ) ;
connection . Closed - = OnConnectionClosed ;
ObjectDisposedException . ThrowIf ( _disposed , this ) ;
try
{
_socketsLock . EnterWriteLock ( ) ;
_sockets . Remove ( connection ) ;
connection . Closed - = OnConnectionClosed ;
}
finally
{
_socketsLock . ExitWriteLock ( ) ;
}
await _sessionManager . CloseIfNeededAsync ( _session ) . ConfigureAwait ( false ) ;
}
@ -69,7 +101,17 @@ namespace Emby.Server.Implementations.Session
T data ,
CancellationToken cancellationToken )
{
var socket = GetActiveSockets ( ) . MaxBy ( i = > i . LastActivityDate ) ;
ObjectDisposedException . ThrowIf ( _disposed , this ) ;
IWebSocketConnection ? socket ;
try
{
_socketsLock . EnterReadLock ( ) ;
socket = _sockets . Where ( i = > i . State = = WebSocketState . Open ) . MaxBy ( i = > i . LastActivityDate ) ;
}
finally
{
_socketsLock . ExitReadLock ( ) ;
}
if ( socket is null )
{
@ -94,12 +136,23 @@ namespace Emby.Server.Implementations.Session
return ;
}
foreach ( var socket in _sockets )
try
{
_socketsLock . EnterWriteLock ( ) ;
foreach ( var socket in _sockets )
{
socket . Closed - = OnConnectionClosed ;
socket . Dispose ( ) ;
}
_sockets . Clear ( ) ;
}
finally
{
socket . Closed - = OnConnectionClosed ;
socket . Dispose ( ) ;
_socketsLock . ExitWriteLock ( ) ;
}
_socketsLock . Dispose ( ) ;
_disposed = true ;
}
@ -110,12 +163,23 @@ namespace Emby.Server.Implementations.Session
return ;
}
foreach ( var socket in _sockets )
try
{
_socketsLock . EnterWriteLock ( ) ;
foreach ( var socket in _sockets )
{
socket . Closed - = OnConnectionClosed ;
await socket . DisposeAsync ( ) . ConfigureAwait ( false ) ;
}
_sockets . Clear ( ) ;
}
finally
{
socket . Closed - = OnConnectionClosed ;
await socket . DisposeAsync ( ) . ConfigureAwait ( false ) ;
_socketsLock . ExitWriteLock ( ) ;
}
_socketsLock . Dispose ( ) ;
_disposed = true ;
}
}