mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
Improve performance, thread safety, and resource handling
Enhanced monitor initialization with parallelism in `MonitorManager.cs` for better performance. Added cancellation support to `NativeEventWaiter.cs` with `CancellationToken` and timeout handling. Introduced thread safety in `PhysicalMonitorHandleManager.cs` using locks to prevent race conditions. Updated `PowerDisplayViewModel.cs` to include proper resource cleanup with `CancellationTokenSource` and improved memory management. Added necessary namespaces for threading and asynchronous operations. General code improvements for readability, maintainability, and reliability.
This commit is contained in:
@@ -104,9 +104,12 @@ namespace PowerDisplay.Core
|
||||
var results = await Task.WhenAll(discoveryTasks);
|
||||
|
||||
// Collect all discovered monitors
|
||||
var allMonitors = new List<Monitor>();
|
||||
|
||||
foreach (var (controller, monitors) in results)
|
||||
{
|
||||
foreach (var monitor in monitors)
|
||||
// Initialize monitors in parallel
|
||||
var initTasks = monitors.Select(async monitor =>
|
||||
{
|
||||
// Verify if monitor can be controlled
|
||||
if (await controller.CanControlMonitorAsync(monitor, cancellationToken))
|
||||
@@ -165,9 +168,14 @@ namespace PowerDisplay.Core
|
||||
}
|
||||
}
|
||||
|
||||
newMonitors.Add(monitor);
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
var initializedMonitors = await Task.WhenAll(initTasks);
|
||||
newMonitors.AddRange(initializedMonitors.Where(m => m != null));
|
||||
}
|
||||
|
||||
// Update monitor list
|
||||
|
||||
@@ -19,15 +19,16 @@ namespace PowerDisplay.Helpers
|
||||
/// </summary>
|
||||
/// <param name="eventName">Name of the Windows Event to wait for</param>
|
||||
/// <param name="callback">Callback to invoke when event is signaled</param>
|
||||
public static void WaitForEventLoop(string eventName, Action callback)
|
||||
/// <param name="cancellationToken">Token to cancel the wait loop</param>
|
||||
public static void WaitForEventLoop(string eventName, Action callback, CancellationToken cancellationToken)
|
||||
{
|
||||
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
var t = new Thread(() =>
|
||||
{
|
||||
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
while (true)
|
||||
using var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (eventHandle.WaitOne())
|
||||
if (eventHandle.WaitOne(500))
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(() => callback());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace PowerDisplay.Native.DDC
|
||||
{
|
||||
// Mapping: deviceKey -> physical handle
|
||||
private readonly Dictionary<string, IntPtr> _deviceKeyToHandleMap = new();
|
||||
private readonly object _lock = new();
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
@@ -24,11 +25,14 @@ namespace PowerDisplay.Native.DDC
|
||||
/// </summary>
|
||||
public IntPtr GetPhysicalHandle(Monitor monitor)
|
||||
{
|
||||
// Primary lookup: use stable deviceKey from EnumDisplayDevices
|
||||
if (!string.IsNullOrEmpty(monitor.DeviceKey) &&
|
||||
_deviceKeyToHandleMap.TryGetValue(monitor.DeviceKey, out var handle))
|
||||
lock (_lock)
|
||||
{
|
||||
return handle;
|
||||
// Primary lookup: use stable deviceKey from EnumDisplayDevices
|
||||
if (!string.IsNullOrEmpty(monitor.DeviceKey) &&
|
||||
_deviceKeyToHandleMap.TryGetValue(monitor.DeviceKey, out var handle))
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: use direct handle from monitor object
|
||||
@@ -51,18 +55,21 @@ namespace PowerDisplay.Native.DDC
|
||||
return (newHandle, false);
|
||||
}
|
||||
|
||||
// Try to reuse existing handle if it's still valid
|
||||
if (_deviceKeyToHandleMap.TryGetValue(deviceKey, out var existingHandle) &&
|
||||
existingHandle != IntPtr.Zero &&
|
||||
DdcCiNative.ValidateDdcCiConnection(existingHandle))
|
||||
lock (_lock)
|
||||
{
|
||||
// Destroy the newly created handle since we're using the old one
|
||||
if (newHandle != existingHandle && newHandle != IntPtr.Zero)
|
||||
// Try to reuse existing handle if it's still valid
|
||||
if (_deviceKeyToHandleMap.TryGetValue(deviceKey, out var existingHandle) &&
|
||||
existingHandle != IntPtr.Zero &&
|
||||
DdcCiNative.ValidateDdcCiConnection(existingHandle))
|
||||
{
|
||||
DestroyPhysicalMonitor(newHandle);
|
||||
}
|
||||
// Destroy the newly created handle since we're using the old one
|
||||
if (newHandle != existingHandle && newHandle != IntPtr.Zero)
|
||||
{
|
||||
DestroyPhysicalMonitor(newHandle);
|
||||
}
|
||||
|
||||
return (existingHandle, true);
|
||||
return (existingHandle, true);
|
||||
}
|
||||
}
|
||||
|
||||
return (newHandle, false);
|
||||
@@ -73,14 +80,17 @@ namespace PowerDisplay.Native.DDC
|
||||
/// </summary>
|
||||
public void UpdateHandleMap(Dictionary<string, IntPtr> newHandleMap)
|
||||
{
|
||||
// Clean up unused handles before updating
|
||||
CleanupUnusedHandles(newHandleMap);
|
||||
|
||||
// Update the device key map
|
||||
_deviceKeyToHandleMap.Clear();
|
||||
foreach (var kvp in newHandleMap)
|
||||
lock (_lock)
|
||||
{
|
||||
_deviceKeyToHandleMap[kvp.Key] = kvp.Value;
|
||||
// Clean up unused handles before updating
|
||||
CleanupUnusedHandles(newHandleMap);
|
||||
|
||||
// Update the device key map
|
||||
_deviceKeyToHandleMap.Clear();
|
||||
foreach (var kvp in newHandleMap)
|
||||
{
|
||||
_deviceKeyToHandleMap[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
@@ -72,7 +73,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
Logger.LogInfo("Received refresh monitors event from PowerDisplay.exe");
|
||||
ReloadMonitorsFromSettings();
|
||||
});
|
||||
},
|
||||
_cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
private void InitializeEnabledValue()
|
||||
@@ -284,6 +286,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_monitors.CollectionChanged -= Monitors_CollectionChanged;
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
@@ -447,6 +453,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private PowerDisplaySettings _settings;
|
||||
private ObservableCollection<MonitorInfo> _monitors;
|
||||
private bool _hasMonitors;
|
||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
public void RefreshEnabledState()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user