Compare commits

...

3 Commits

2 changed files with 28 additions and 2 deletions

View File

@@ -465,7 +465,20 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
private void ThemeServiceOnThemeChanged(object? sender, ThemeChangedEventArgs e)
{
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
// Call Reapply directly rather than via _saveTimer.Debounce.
// ThemeService already debounces internally, so ThemeChanged is already
// de-duplicated by the time this handler runs. Using Debounce here can
// cause a deadlock: a prior Debounce call's DispatcherQueueTimer may still
// be trying to unsubscribe from its Tick event, which acquires a WinRT
// EventSourceCache write lock. While waiting for that write lock (blocked
// by background threads holding read locks during extension-event subscription),
// WinRT STA reentrancy lets a nested timer callback reach this handler again.
// The inner Debounce call then tries to subscribe to _saveTimer.Tick (a read
// lock), but ReaderWriterLockSlim blocks new readers while a writer is pending,
// creating a circular wait: the pending writer waits for background readers to
// finish, and those background threads wait for the STA to process a COM call
// that is itself blocked by the deadlocked nested callback.
Reapply();
}
private void DebouncedReapply()

View File

@@ -295,7 +295,20 @@ public sealed partial class DockAppearanceSettingsViewModel : ObservableObject,
private void ThemeServiceOnThemeChanged(object? sender, ThemeChangedEventArgs e)
{
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
// Call Reapply directly rather than via _saveTimer.Debounce.
// ThemeService already debounces internally, so ThemeChanged is already
// de-duplicated by the time this handler runs. Using Debounce here can
// cause a deadlock: a prior Debounce call's DispatcherQueueTimer may still
// be trying to unsubscribe from its Tick event, which acquires a WinRT
// EventSourceCache write lock. While waiting for that write lock (blocked
// by background threads holding read locks during extension-event subscription),
// WinRT STA reentrancy lets a nested timer callback reach this handler again.
// The inner Debounce call then tries to subscribe to _saveTimer.Tick (a read
// lock), but ReaderWriterLockSlim blocks new readers while a writer is pending,
// creating a circular wait: the pending writer waits for background readers to
// finish, and those background threads wait for the STA to process a COM call
// that is itself blocked by the deadlocked nested callback.
Reapply();
}
private void DebouncedReapply()