aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs')
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs69
1 files changed, 67 insertions, 2 deletions
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
index b88ee33358..88dfb070b8 100644
--- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs
@@ -19,6 +19,11 @@ namespace Emby.Server.Implementations.SyncPlay
public class SyncPlayManager : ISyncPlayManager, IDisposable
{
/// <summary>
+ /// How often, in milliseconds, the groups are checked for a spent wait deadline.
+ /// </summary>
+ private const int GroupWaitSweepInterval = 1000;
+
+ /// <summary>
/// The logger.
/// </summary>
private readonly ILogger<SyncPlayManager> _logger;
@@ -69,6 +74,11 @@ namespace Emby.Server.Implementations.SyncPlay
/// </remarks>
private readonly Lock _groupsLock = new();
+ /// <summary>
+ /// The timer that watches the groups' wait deadlines, running only while there are groups.
+ /// </summary>
+ private readonly Timer _groupWaitTimer;
+
private bool _disposed = false;
/// <summary>
@@ -90,8 +100,15 @@ namespace Emby.Server.Implementations.SyncPlay
_libraryManager = libraryManager;
_logger = loggerFactory.CreateLogger<SyncPlayManager>();
_sessionManager.SessionEnded += OnSessionEnded;
+ _groupWaitTimer = new Timer(_ => OnGroupWaitTimerTick(), null, Timeout.Infinite, Timeout.Infinite);
}
+ /// <summary>
+ /// Gets the maximum time, in milliseconds, a group waits for its members to report ready.
+ /// </summary>
+ /// <value>The group-wait timeout.</value>
+ internal long GroupWaitTimeout { get; init; } = Group.DefaultGroupWaitTimeout;
+
/// <inheritdoc />
public void Dispose()
{
@@ -122,8 +139,12 @@ namespace Emby.Server.Implementations.SyncPlay
LeaveGroup(session, leaveGroupRequest, cancellationToken);
}
- var group = new Group(_loggerFactory, _userManager, _sessionManager, _libraryManager);
+ var group = new Group(_loggerFactory, _userManager, _sessionManager, _libraryManager)
+ {
+ GroupWaitTimeout = GroupWaitTimeout
+ };
_groups[group.GroupId] = group;
+ UpdateGroupWaitTimer();
if (!_sessionToGroupMap.TryAdd(session.Id, group))
{
@@ -242,6 +263,7 @@ namespace Emby.Server.Implementations.SyncPlay
{
_logger.LogInformation("Group {GroupId} is empty, removing it.", group.GroupId);
_groups.Remove(group.GroupId, out _);
+ UpdateGroupWaitTimer();
}
}
}
@@ -384,7 +406,50 @@ namespace Emby.Server.Implementations.SyncPlay
}
_sessionManager.SessionEnded -= OnSessionEnded;
- _disposed = true;
+
+ lock (_groupsLock)
+ {
+ _disposed = true;
+ _groupWaitTimer.Dispose();
+ }
+ }
+
+ private void UpdateGroupWaitTimer()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ var interval = _groups.IsEmpty ? Timeout.Infinite : GroupWaitSweepInterval;
+ _groupWaitTimer.Change(interval, interval);
+ }
+
+ private void OnGroupWaitTimerTick()
+ {
+ try
+ {
+ lock (_groupsLock)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ foreach (var (_, group) in _groups)
+ {
+ // Group lock required as Group is not thread-safe.
+ lock (group)
+ {
+ group.HandleGroupWaitTimeout(CancellationToken.None);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error while recovering SyncPlay groups from a timed out wait.");
+ }
}
private void OnSessionEnded(object sender, SessionEventArgs e)