From 84ddc788db04fea3e073a5dbc4b9a6dedb37f658 Mon Sep 17 00:00:00 2001 From: Antoine Aflalo Date: Sat, 8 May 2021 13:05:09 -0400 Subject: [PATCH] fix(DeviceLister): Fix concurrency issue with the TrayIcon Fixes #626 Fixes #622 --- .../Audio/Lister/CachedAudioDeviceLister.cs | 28 +++++++++++++------ SoundSwitch/SoundSwitch.csproj | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs b/SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs index 3133f43735..159479b732 100644 --- a/SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs +++ b/SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs @@ -13,7 +13,9 @@ ********************************************************************/ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using NAudio.CoreAudioApi; using Serilog; using SoundSwitch.Common.Framework.Audio.Device; @@ -26,16 +28,16 @@ namespace SoundSwitch.Framework.Audio.Lister public class CachedAudioDeviceLister : IAudioDeviceLister { /// - public IReadOnlyCollection PlaybackDevices => _playbackDevices; + public IReadOnlyCollection PlaybackDevices => (IReadOnlyCollection) _playbackDevices.Values; /// - public IReadOnlyCollection RecordingDevices => _recordingDevices; + public IReadOnlyCollection RecordingDevices => (IReadOnlyCollection) _recordingDevices.Values; private readonly DeviceState _state; private readonly DebounceDispatcher _dispatcher = new(); private readonly object _lock = new(); - private readonly HashSet _playbackDevices = new(); - private readonly HashSet _recordingDevices = new(); + private readonly ConcurrentDictionary _playbackDevices = new(); + private readonly ConcurrentDictionary _recordingDevices = new(); public CachedAudioDeviceLister(DeviceState state) { @@ -51,12 +53,12 @@ public void Refresh() { lock (_lock) { - _recordingDevices.Clear(); - _playbackDevices.Clear(); + var ids = new HashSet(); Log.Information("[{@State}] Refreshing all devices", _state); using var enumerator = new MMDeviceEnumerator(); foreach (var endPoint in enumerator.EnumerateAudioEndPoints(DataFlow.All, _state)) { + ids.Add(endPoint.ID); try { var deviceInfo = new DeviceFullInfo(endPoint); @@ -68,10 +70,10 @@ public void Refresh() switch (deviceInfo.Type) { case DataFlow.Render: - _playbackDevices.Add(deviceInfo); + _playbackDevices.AddOrUpdate(deviceInfo.Id, deviceInfo, (_, _) => deviceInfo); break; case DataFlow.Capture: - _recordingDevices.Add(deviceInfo); + _recordingDevices.AddOrUpdate(deviceInfo.Id, deviceInfo, (_, _) => deviceInfo); break; case DataFlow.All: break; @@ -85,6 +87,16 @@ public void Refresh() } } + foreach (var deviceId in _playbackDevices.Keys.Except(ids)) + { + _playbackDevices.TryRemove(deviceId, out _); + } + + foreach (var deviceId in _recordingDevices.Keys.Except(ids)) + { + _recordingDevices.TryRemove(deviceId, out _); + } + Log.Information("[{@State}] Refreshed all devices. {@Recording}/rec, {@Playback}/play", _state, _recordingDevices.Count, _playbackDevices.Count); } } diff --git a/SoundSwitch/SoundSwitch.csproj b/SoundSwitch/SoundSwitch.csproj index 73bb8ddfe1..d54f044366 100644 --- a/SoundSwitch/SoundSwitch.csproj +++ b/SoundSwitch/SoundSwitch.csproj @@ -18,6 +18,8 @@ 8.0 + TRACE;DEBUG + false true