mirror of https://github.com/jellyfin/jellyfin.git
Changes SessionWebSocketListener to (re)use a timer (#11358)
This commit is contained in:
parent
82e5f99f83
commit
356e05e3af
|
@ -101,14 +101,14 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
var pipe = new Pipe();
|
var pipe = new Pipe();
|
||||||
var writer = pipe.Writer;
|
var writer = pipe.Writer;
|
||||||
|
|
||||||
ValueWebSocketReceiveResult receiveresult;
|
ValueWebSocketReceiveResult receiveResult;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Allocate at least 512 bytes from the PipeWriter
|
// Allocate at least 512 bytes from the PipeWriter
|
||||||
Memory<byte> memory = writer.GetMemory(512);
|
Memory<byte> memory = writer.GetMemory(512);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
receiveresult = await _socket.ReceiveAsync(memory, cancellationToken).ConfigureAwait(false);
|
receiveResult = await _socket.ReceiveAsync(memory, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (WebSocketException ex)
|
catch (WebSocketException ex)
|
||||||
{
|
{
|
||||||
|
@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytesRead = receiveresult.Count;
|
int bytesRead = receiveResult.Count;
|
||||||
if (bytesRead == 0)
|
if (bytesRead == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@ -135,13 +135,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
LastActivityDate = DateTime.UtcNow;
|
LastActivityDate = DateTime.UtcNow;
|
||||||
|
|
||||||
if (receiveresult.EndOfMessage)
|
if (receiveResult.EndOfMessage)
|
||||||
{
|
{
|
||||||
await ProcessInternal(pipe.Reader).ConfigureAwait(false);
|
await ProcessInternal(pipe.Reader).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while ((_socket.State == WebSocketState.Open || _socket.State == WebSocketState.Connecting)
|
while ((_socket.State == WebSocketState.Open || _socket.State == WebSocketState.Connecting)
|
||||||
&& receiveresult.MessageType != WebSocketMessageType.Close);
|
&& receiveResult.MessageType != WebSocketMessageType.Close);
|
||||||
|
|
||||||
Closed?.Invoke(this, EventArgs.Empty);
|
Closed?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,6 @@ namespace Emby.Server.Implementations.Session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float ForceKeepAliveFactor = 0.75f;
|
private const float ForceKeepAliveFactor = 0.75f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lock used for accessing the KeepAlive cancellation token.
|
|
||||||
/// </summary>
|
|
||||||
private readonly object _keepAliveLock = new object();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The WebSocket watchlist.
|
/// The WebSocket watchlist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -55,7 +50,7 @@ namespace Emby.Server.Implementations.Session
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The KeepAlive cancellation token.
|
/// The KeepAlive cancellation token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private CancellationTokenSource? _keepAliveCancellationToken;
|
private System.Timers.Timer _keepAlive;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
|
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
|
||||||
|
@ -71,12 +66,34 @@ namespace Emby.Server.Implementations.Session
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
|
_keepAlive = new System.Timers.Timer(TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor))
|
||||||
|
{
|
||||||
|
AutoReset = true,
|
||||||
|
Enabled = false
|
||||||
|
};
|
||||||
|
_keepAlive.Elapsed += KeepAliveSockets;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
StopKeepAlive();
|
if (_keepAlive is not null)
|
||||||
|
{
|
||||||
|
_keepAlive.Stop();
|
||||||
|
_keepAlive.Elapsed -= KeepAliveSockets;
|
||||||
|
_keepAlive.Dispose();
|
||||||
|
_keepAlive = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_webSocketsLock)
|
||||||
|
{
|
||||||
|
foreach (var webSocket in _webSockets)
|
||||||
|
{
|
||||||
|
webSocket.Closed -= OnWebSocketClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_webSockets.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -164,7 +181,7 @@ namespace Emby.Server.Implementations.Session
|
||||||
webSocket.Closed += OnWebSocketClosed;
|
webSocket.Closed += OnWebSocketClosed;
|
||||||
webSocket.LastKeepAliveDate = DateTime.UtcNow;
|
webSocket.LastKeepAliveDate = DateTime.UtcNow;
|
||||||
|
|
||||||
StartKeepAlive();
|
_keepAlive.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify WebSocket about timeout
|
// Notify WebSocket about timeout
|
||||||
|
@ -186,66 +203,26 @@ namespace Emby.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
lock (_webSocketsLock)
|
lock (_webSocketsLock)
|
||||||
{
|
{
|
||||||
if (!_webSockets.Remove(webSocket))
|
if (_webSockets.Remove(webSocket))
|
||||||
{
|
{
|
||||||
_logger.LogWarning("WebSocket {0} not on watchlist.", webSocket);
|
webSocket.Closed -= OnWebSocketClosed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
webSocket.Closed -= OnWebSocketClosed;
|
_logger.LogWarning("WebSocket {0} not on watchlist.", webSocket);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
if (_webSockets.Count == 0)
|
||||||
/// Starts the KeepAlive watcher.
|
|
||||||
/// </summary>
|
|
||||||
private void StartKeepAlive()
|
|
||||||
{
|
{
|
||||||
lock (_keepAliveLock)
|
_keepAlive.Stop();
|
||||||
{
|
|
||||||
if (_keepAliveCancellationToken is null)
|
|
||||||
{
|
|
||||||
_keepAliveCancellationToken = new CancellationTokenSource();
|
|
||||||
// Start KeepAlive watcher
|
|
||||||
_ = RepeatAsyncCallbackEvery(
|
|
||||||
KeepAliveSockets,
|
|
||||||
TimeSpan.FromSeconds(WebSocketLostTimeout * IntervalFactor),
|
|
||||||
_keepAliveCancellationToken.Token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops the KeepAlive watcher.
|
|
||||||
/// </summary>
|
|
||||||
private void StopKeepAlive()
|
|
||||||
{
|
|
||||||
lock (_keepAliveLock)
|
|
||||||
{
|
|
||||||
if (_keepAliveCancellationToken is not null)
|
|
||||||
{
|
|
||||||
_keepAliveCancellationToken.Cancel();
|
|
||||||
_keepAliveCancellationToken.Dispose();
|
|
||||||
_keepAliveCancellationToken = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_webSocketsLock)
|
|
||||||
{
|
|
||||||
foreach (var webSocket in _webSockets)
|
|
||||||
{
|
|
||||||
webSocket.Closed -= OnWebSocketClosed;
|
|
||||||
}
|
|
||||||
|
|
||||||
_webSockets.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks status of KeepAlive of WebSockets.
|
/// Checks status of KeepAlive of WebSockets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task KeepAliveSockets()
|
private async void KeepAliveSockets(object? o, EventArgs? e)
|
||||||
{
|
{
|
||||||
List<IWebSocketConnection> inactive;
|
List<IWebSocketConnection> inactive;
|
||||||
List<IWebSocketConnection> lost;
|
List<IWebSocketConnection> lost;
|
||||||
|
@ -291,11 +268,6 @@ namespace Emby.Server.Implementations.Session
|
||||||
RemoveWebSocket(webSocket);
|
RemoveWebSocket(webSocket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_webSockets.Count == 0)
|
|
||||||
{
|
|
||||||
StopKeepAlive();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,29 +282,5 @@ namespace Emby.Server.Implementations.Session
|
||||||
new ForceKeepAliveMessage(WebSocketLostTimeout),
|
new ForceKeepAliveMessage(WebSocketLostTimeout),
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs a given async callback once every specified interval time, until cancelled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="callback">The async callback.</param>
|
|
||||||
/// <param name="interval">The interval time.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
private async Task RepeatAsyncCallbackEvery(Func<Task> callback, TimeSpan interval, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await callback().ConfigureAwait(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(interval, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue