Don't take a lock if there's no intention to manipulate the list of open streams. Instead, use a ConcurrentDictionary so that, in those situations, thread-safe access to the dictionary is ensured.

This commit is contained in:
Ryan Petris 2020-09-23 14:12:26 -07:00
parent e92d8dd195
commit 4db5700e18
No known key found for this signature in database
GPG Key ID: C3D15EFA013E18C2

View File

@ -1,6 +1,7 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
private IMediaSourceProvider[] _providers; private IMediaSourceProvider[] _providers;
@ -582,29 +583,20 @@ namespace Emby.Server.Implementations.Library
mediaSource.InferTotalBitrate(); mediaSource.InferTotalBitrate();
} }
public async Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken) public Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
{ {
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); var info = _openStreams.Values.FirstOrDefault(i =>
try
{ {
var info = _openStreams.Values.FirstOrDefault(i => var liveStream = i as ILiveStream;
if (liveStream != null)
{ {
var liveStream = i as ILiveStream; return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
if (liveStream != null) }
{
return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
}
return false; return false;
}); });
return info as IDirectStreamProvider; return Task.FromResult(info as IDirectStreamProvider);
}
finally
{
_liveStreamSemaphore.Release();
}
} }
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken) public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
@ -793,29 +785,20 @@ namespace Emby.Server.Implementations.Library
return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider); return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider);
} }
private async Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken) private Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken)
{ {
if (string.IsNullOrEmpty(id)) if (string.IsNullOrEmpty(id))
{ {
throw new ArgumentNullException(nameof(id)); throw new ArgumentNullException(nameof(id));
} }
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); if (_openStreams.TryGetValue(id, out ILiveStream info))
try
{ {
if (_openStreams.TryGetValue(id, out ILiveStream info)) return Task.FromResult(info);
{
return info;
}
else
{
throw new ResourceNotFoundException();
}
} }
finally else
{ {
_liveStreamSemaphore.Release(); return Task.FromException<ILiveStream>(new ResourceNotFoundException());
} }
} }
@ -844,7 +827,7 @@ namespace Emby.Server.Implementations.Library
if (liveStream.ConsumerCount <= 0) if (liveStream.ConsumerCount <= 0)
{ {
_openStreams.Remove(id); _openStreams.TryRemove(id, out _);
_logger.LogInformation("Closing live stream {0}", id); _logger.LogInformation("Closing live stream {0}", id);