diff --git a/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs b/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs index e76554e..2d1f2eb 100644 --- a/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs +++ b/RGB.NET.Core/Devices/AbstractRGBDeviceProvider.cs @@ -12,6 +12,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider { #region Properties & Fields + private bool _isDisposed = false; + private readonly double _defaultUpdateRateHardLimit; /// @@ -60,6 +62,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit; } + ~AbstractRGBDeviceProvider() => Dispose(false); + #endregion #region Methods @@ -67,6 +71,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false) { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + ThrowsExceptions = throwExceptions; try @@ -108,6 +114,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// The filtered collection of loaded devices. protected virtual IEnumerable GetLoadedDevices(RGBDeviceType loadFilter) { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + List devices = new(); foreach (IRGBDevice device in LoadDevices()) { @@ -152,6 +160,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// The update trigger mapped to the specified id. protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null) { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger)) UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit)); @@ -171,6 +181,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// protected virtual void Reset() { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values) updateTrigger.Dispose(); @@ -192,6 +204,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// true if the device was added successfully; otherwise false. protected virtual bool AddDevice(IRGBDevice device) { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + if (InternalDevices.Contains(device)) return false; InternalDevices.Add(device); @@ -207,6 +221,8 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider /// true if the device was removed successfully; otherwise false. protected virtual bool RemoveDevice(IRGBDevice device) { + if (_isDisposed) throw new ObjectDisposedException(GetType().FullName); + if (!InternalDevices.Remove(device)) return false; try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesRemovedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ } @@ -241,12 +257,26 @@ public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider protected virtual void OnDevicesChanged(DevicesChangedEventArgs args) => DevicesChanged?.Invoke(this, args); /// - public virtual void Dispose() + public void Dispose() { - Reset(); + if (_isDisposed) return; + + try + { + Dispose(true); + } + catch { /* don't throw in dispose! */ } GC.SuppressFinalize(this); + + _isDisposed = true; } + /// + /// Disposes the object and frees all resources. + /// + /// true if explicitely called through the Dispose-Method, false if called by the destructor. + protected virtual void Dispose(bool disposing) => Reset(); + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Asus/AsusDeviceProvider.cs b/RGB.NET.Devices.Asus/AsusDeviceProvider.cs index e3b093c..805db68 100644 --- a/RGB.NET.Devices.Asus/AsusDeviceProvider.cs +++ b/RGB.NET.Devices.Asus/AsusDeviceProvider.cs @@ -16,11 +16,21 @@ public sealed class AsusDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static AsusDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static AsusDeviceProvider Instance => _instance ?? new AsusDeviceProvider(); + public static AsusDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new AsusDeviceProvider(); + } + } private IAuraSdk2? _sdk; private IAuraSyncDeviceCollection? _devices; //HACK DarthAffe 05.04.2021: Due to some researches this might fix the access violation in the asus-sdk @@ -35,8 +45,11 @@ public sealed class AsusDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public AsusDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(AsusDeviceProvider)}"); + _instance = this; + } } #endregion @@ -80,15 +93,19 @@ public sealed class AsusDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { _sdk?.ReleaseControl(0); } - catch { /* at least we tried */ } + try { _sdk?.ReleaseControl(0); } + catch { /* at least we tried */ } - _devices = null; - _sdk = null; + _devices = null; + _sdk = null; + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs index ab16a45..8678e1a 100644 --- a/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs +++ b/RGB.NET.Devices.CoolerMaster/CoolerMasterDeviceProvider.cs @@ -17,11 +17,21 @@ public sealed class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static CoolerMasterDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static CoolerMasterDeviceProvider Instance => _instance ?? new CoolerMasterDeviceProvider(); + public static CoolerMasterDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new CoolerMasterDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. @@ -45,8 +55,11 @@ public sealed class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public CoolerMasterDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CoolerMasterDeviceProvider)}"); + _instance = this; + } } #endregion @@ -94,12 +107,17 @@ public sealed class CoolerMasterDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { _CoolerMasterSDK.Reload(); } - catch { /* Unlucky.. */ } + try { _CoolerMasterSDK.Reload(); } + catch { /* Unlucky.. */ } + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs index 87ab725..89f4684 100644 --- a/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs +++ b/RGB.NET.Devices.Corsair/CorsairDeviceProvider.cs @@ -17,11 +17,21 @@ public sealed class CorsairDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static CorsairDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static CorsairDeviceProvider Instance => _instance ?? new CorsairDeviceProvider(); + public static CorsairDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new CorsairDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. @@ -80,8 +90,11 @@ public sealed class CorsairDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public CorsairDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(CorsairDeviceProvider)}"); + _instance = this; + } } #endregion @@ -300,12 +313,20 @@ public sealed class CorsairDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { _CUESDK.CorsairDisconnect(); } catch { /* at least we tried */ } - try { _CUESDK.UnloadCUESDK(); } catch { /* at least we tried */ } + try { _CUESDK.CorsairDisconnect(); } + catch { /* at least we tried */ } + + try { _CUESDK.UnloadCUESDK(); } + catch { /* at least we tried */ } + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs index 2d85a44..d5f2456 100644 --- a/RGB.NET.Devices.DMX/DMXDeviceProvider.cs +++ b/RGB.NET.Devices.DMX/DMXDeviceProvider.cs @@ -16,11 +16,21 @@ public sealed class DMXDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static DMXDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static DMXDeviceProvider Instance => _instance ?? new DMXDeviceProvider(); + public static DMXDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new DMXDeviceProvider(); + } + } /// /// Gets a list of all defined device-definitions. @@ -37,8 +47,11 @@ public sealed class DMXDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public DMXDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DMXDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DMXDeviceProvider)}"); + _instance = this; + } } #endregion @@ -86,5 +99,16 @@ public sealed class DMXDeviceProvider : AbstractRGBDeviceProvider return updateTrigger; } + /// + protected override void Dispose(bool disposing) + { + lock (_lock) + { + base.Dispose(disposing); + + _instance = null; + } + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Debug/DebugDeviceProvider.cs b/RGB.NET.Devices.Debug/DebugDeviceProvider.cs index f88ad50..836c6db 100644 --- a/RGB.NET.Devices.Debug/DebugDeviceProvider.cs +++ b/RGB.NET.Devices.Debug/DebugDeviceProvider.cs @@ -16,11 +16,21 @@ public sealed class DebugDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static DebugDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static DebugDeviceProvider Instance => _instance ?? new DebugDeviceProvider(); + public static DebugDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new DebugDeviceProvider(); + } + } private List<(IDeviceLayout layout, Action>? updateLedsAction)> _fakeDeviceDefinitions = new(); @@ -34,8 +44,11 @@ public sealed class DebugDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public DebugDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DebugDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(DebugDeviceProvider)}"); + _instance = this; + } } #endregion @@ -66,11 +79,16 @@ public sealed class DebugDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - _fakeDeviceDefinitions.Clear(); + _fakeDeviceDefinitions.Clear(); + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs index 4ebd163..4a12b39 100644 --- a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs +++ b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs @@ -20,11 +20,21 @@ public class LogitechDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static LogitechDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static LogitechDeviceProvider Instance => _instance ?? new LogitechDeviceProvider(); + public static LogitechDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new LogitechDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. @@ -112,7 +122,7 @@ public class LogitechDeviceProvider : AbstractRGBDeviceProvider { 0x0A78, RGBDeviceType.Speaker, "G560", LedMappings.ZoneSpeaker, (LogitechDeviceType.Speaker, 4, 0) }, }; - + /// /// Gets the HID-definitions for wireless per-zone-devices. /// @@ -164,8 +174,12 @@ public class LogitechDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public LogitechDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(LogitechDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) + throw new InvalidOperationException($"There can be only one instance of type {nameof(LogitechDeviceProvider)}"); + _instance = this; + } } #endregion @@ -254,17 +268,20 @@ public class LogitechDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { _LogitechGSDK.LogiLedRestoreLighting(); } - catch { /* at least we tried */ } + try { _LogitechGSDK.LogiLedRestoreLighting(); } + catch { /* at least we tried */ } - try { _LogitechGSDK.UnloadLogitechGSDK(); } - catch { /* at least we tried */ } + try { _LogitechGSDK.UnloadLogitechGSDK(); } + catch { /* at least we tried */ } - GC.SuppressFinalize(this); + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.Msi/MsiDeviceProvider.cs b/RGB.NET.Devices.Msi/MsiDeviceProvider.cs index fb18827..1a305cc 100644 --- a/RGB.NET.Devices.Msi/MsiDeviceProvider.cs +++ b/RGB.NET.Devices.Msi/MsiDeviceProvider.cs @@ -17,11 +17,21 @@ public class MsiDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static MsiDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static MsiDeviceProvider Instance => _instance ?? new MsiDeviceProvider(); + public static MsiDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new MsiDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. @@ -45,8 +55,11 @@ public class MsiDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public MsiDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(MsiDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(MsiDeviceProvider)}"); + _instance = this; + } } #endregion @@ -98,14 +111,17 @@ public class MsiDeviceProvider : AbstractRGBDeviceProvider private void ThrowMsiError(int errorCode, bool isCritical = false) => Throw(new MysticLightException(errorCode, _MsiSDK.GetErrorMessage(errorCode)), isCritical); /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { _MsiSDK.UnloadMsiSDK(); } - catch { /* at least we tried */ } + try { _MsiSDK.UnloadMsiSDK(); } + catch { /* at least we tried */ } - GC.SuppressFinalize(this); + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs index 837a82c..9a86f6f 100644 --- a/RGB.NET.Devices.Novation/NovationDeviceProvider.cs +++ b/RGB.NET.Devices.Novation/NovationDeviceProvider.cs @@ -17,11 +17,21 @@ public sealed class NovationDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static NovationDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static NovationDeviceProvider Instance => _instance ?? new NovationDeviceProvider(); + public static NovationDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new NovationDeviceProvider(); + } + } #endregion @@ -33,8 +43,11 @@ public sealed class NovationDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. private NovationDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(NovationDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(NovationDeviceProvider)}"); + _instance = this; + } } #endregion @@ -67,5 +80,16 @@ public sealed class NovationDeviceProvider : AbstractRGBDeviceProvider } } + /// + protected override void Dispose(bool disposing) + { + lock (_lock) + { + base.Dispose(disposing); + + _instance = null; + } + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.OpenRGB/OpenRGBDeviceProvider.cs b/RGB.NET.Devices.OpenRGB/OpenRGBDeviceProvider.cs index 4d19484..4ad82cc 100644 --- a/RGB.NET.Devices.OpenRGB/OpenRGBDeviceProvider.cs +++ b/RGB.NET.Devices.OpenRGB/OpenRGBDeviceProvider.cs @@ -14,6 +14,9 @@ public sealed class OpenRGBDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private readonly List _clients = new(); private static OpenRGBDeviceProvider? _instance; @@ -21,7 +24,14 @@ public sealed class OpenRGBDeviceProvider : AbstractRGBDeviceProvider /// /// Gets the singleton instance. /// - public static OpenRGBDeviceProvider Instance => _instance ?? new OpenRGBDeviceProvider(); + public static OpenRGBDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new OpenRGBDeviceProvider(); + } + } /// /// Gets a list of all defined device-definitions. @@ -48,8 +58,11 @@ public sealed class OpenRGBDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public OpenRGBDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(OpenRGBDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(OpenRGBDeviceProvider)}"); + _instance = this; + } } #endregion @@ -107,22 +120,22 @@ public sealed class OpenRGBDeviceProvider : AbstractRGBDeviceProvider continue; } - if (device.Zones.Length == 0) + if (device.Zones.Length == 0) continue; - if (device.Zones.All(z => z.LedCount == 0)) + if (device.Zones.All(z => z.LedCount == 0)) continue; OpenRGBUpdateQueue updateQueue = new(GetUpdateTrigger(), i, openRgb, device); - + bool anyZoneHasSegments = device.Zones.Any(z => z.Segments.Length > 0); - bool splitDeviceByZones = anyZoneHasSegments || PerZoneDeviceFlag.HasFlag(Helper.GetRgbNetDeviceType(device.Type)); + bool splitDeviceByZones = anyZoneHasSegments || PerZoneDeviceFlag.HasFlag(Helper.GetRgbNetDeviceType(device.Type)); if (!splitDeviceByZones) { yield return new OpenRGBGenericDevice(new OpenRGBDeviceInfo(device), updateQueue); continue; } - + int totalLedCount = 0; foreach (Zone zone in device.Zones) @@ -149,18 +162,23 @@ public sealed class OpenRGBDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); - - foreach (OpenRgbClient client in _clients) + lock (_lock) { - try { client.Dispose(); } - catch { /* at least we tried */ } - } + base.Dispose(disposing); - _clients.Clear(); - DeviceDefinitions.Clear(); + foreach (OpenRgbClient client in _clients) + { + try { client.Dispose(); } + catch { /* at least we tried */ } + } + + _clients.Clear(); + DeviceDefinitions.Clear(); + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.PicoPi/PicoPiDeviceProvider.cs b/RGB.NET.Devices.PicoPi/PicoPiDeviceProvider.cs index f18116f..c799460 100644 --- a/RGB.NET.Devices.PicoPi/PicoPiDeviceProvider.cs +++ b/RGB.NET.Devices.PicoPi/PicoPiDeviceProvider.cs @@ -25,11 +25,21 @@ public sealed class PicoPiDeviceProvider : AbstractRGBDeviceProvider #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static PicoPiDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static PicoPiDeviceProvider Instance => _instance ?? new PicoPiDeviceProvider(); + public static PicoPiDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new PicoPiDeviceProvider(); + } + } /// /// Gets the HID-definitions for PicoPi-devices. @@ -57,8 +67,11 @@ public sealed class PicoPiDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public PicoPiDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(PicoPiDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(PicoPiDeviceProvider)}"); + _instance = this; + } } #endregion @@ -126,5 +139,16 @@ public sealed class PicoPiDeviceProvider : AbstractRGBDeviceProvider _sdks.Clear(); } + /// + protected override void Dispose(bool disposing) + { + lock (_lock) + { + base.Dispose(disposing); + + _instance = null; + } + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs index 15f2657..8dfadc4 100644 --- a/RGB.NET.Devices.Razer/RazerDeviceProvider.cs +++ b/RGB.NET.Devices.Razer/RazerDeviceProvider.cs @@ -19,11 +19,21 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static RazerDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static RazerDeviceProvider Instance => _instance ?? new RazerDeviceProvider(); + public static RazerDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new RazerDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 applications. @@ -255,7 +265,7 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider { 0x0F13, RGBDeviceType.Unknown, "Lian Li O11", LedMappings.ChromaLink, RazerEndpointType.ChromaLink }, { 0x0F1D, RGBDeviceType.Unknown, "Mouse Bungee V3 Chroma", LedMappings.ChromaLink, RazerEndpointType.ChromaLink }, { 0x0F1F, RGBDeviceType.LedController, "Addressable RGB Controller", LedMappings.ChromaLink, RazerEndpointType.ChromaLink }, - + }; #endregion @@ -268,8 +278,11 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public RazerDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RazerDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(RazerDeviceProvider)}"); + _instance = this; + } } #endregion @@ -343,15 +356,20 @@ public sealed class RazerDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - TryUnInit(); + TryUnInit(); - // DarthAffe 03.03.2020: Fails with an access-violation - verify if an unload is already triggered by uninit - //try { _RazerSDK.UnloadRazerSDK(); } - //catch { /* at least we tried */ } + // DarthAffe 03.03.2020: Fails with an access-violation - verify if an unload is already triggered by uninit + //try { _RazerSDK.UnloadRazerSDK(); } + //catch { /* at least we tried */ } + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs index 61f3254..0bf1958 100644 --- a/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs +++ b/RGB.NET.Devices.SteelSeries/SteelSeriesDeviceProvider.cs @@ -20,11 +20,21 @@ public sealed class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static SteelSeriesDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static SteelSeriesDeviceProvider Instance => _instance ?? new SteelSeriesDeviceProvider(); + public static SteelSeriesDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new SteelSeriesDeviceProvider(); + } + } private const int VENDOR_ID = 0x1038; @@ -93,8 +103,11 @@ public sealed class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public SteelSeriesDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(SteelSeriesDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(SteelSeriesDeviceProvider)}"); + _instance = this; + } } #endregion @@ -133,12 +146,17 @@ public sealed class SteelSeriesDeviceProvider : AbstractRGBDeviceProvider protected override IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit) { HeartbeatTimer = HEARTBEAT_TIMER }; /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - try { SteelSeriesSDK.Dispose(); } - catch { /* shit happens */ } + try { SteelSeriesSDK.Dispose(); } + catch { /* shit happens */ } + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs b/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs index 4d2e047..e150028 100644 --- a/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs +++ b/RGB.NET.Devices.WS281X/WS281XDeviceProvider.cs @@ -16,11 +16,21 @@ public sealed class WS281XDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static WS281XDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static WS281XDeviceProvider Instance => _instance ?? new WS281XDeviceProvider(); + public static WS281XDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new WS281XDeviceProvider(); + } + } /// /// Gets a list of all defined device-definitions. @@ -39,8 +49,11 @@ public sealed class WS281XDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public WS281XDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WS281XDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WS281XDeviceProvider)}"); + _instance = this; + } } #endregion @@ -70,11 +83,16 @@ public sealed class WS281XDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); + lock (_lock) + { + base.Dispose(disposing); - DeviceDefinitions.Clear(); + DeviceDefinitions.Clear(); + + _instance = null; + } } #endregion diff --git a/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs b/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs index 484bbaf..6d16b2f 100644 --- a/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs +++ b/RGB.NET.Devices.Wooting/WootingDeviceProvider.cs @@ -16,11 +16,21 @@ public sealed class WootingDeviceProvider : AbstractRGBDeviceProvider { #region Properties & Fields + // ReSharper disable once InconsistentNaming + private static readonly object _lock = new(); + private static WootingDeviceProvider? _instance; /// /// Gets the singleton instance. /// - public static WootingDeviceProvider Instance => _instance ?? new WootingDeviceProvider(); + public static WootingDeviceProvider Instance + { + get + { + lock (_lock) + return _instance ?? new WootingDeviceProvider(); + } + } /// /// Gets a modifiable list of paths used to find the native SDK-dlls for x86 windows applications. @@ -57,8 +67,11 @@ public sealed class WootingDeviceProvider : AbstractRGBDeviceProvider /// Thrown if this constructor is called even if there is already an instance of this class. public WootingDeviceProvider() { - if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WootingDeviceProvider)}"); - _instance = this; + lock (_lock) + { + if (_instance != null) throw new InvalidOperationException($"There can be only one instance of type {nameof(WootingDeviceProvider)}"); + _instance = this; + } } #endregion @@ -94,14 +107,19 @@ public sealed class WootingDeviceProvider : AbstractRGBDeviceProvider } /// - public override void Dispose() + protected override void Dispose(bool disposing) { - base.Dispose(); - - lock (_WootingSDK.SdkLock) + lock (_lock) { - try { _WootingSDK.UnloadWootingSDK(); } - catch { /* at least we tried */ } + base.Dispose(disposing); + + lock (_WootingSDK.SdkLock) + { + try { _WootingSDK.UnloadWootingSDK(); } + catch { /* at least we tried */ } + } + + _instance = null; } }