fix(selftests): take TabsListLock around Config.Tabs mutations
The three sidebar/coupling steps add, insert and remove tabs straight from the framework thread while the message worker mutates the same list under TabsListLock. CurrentTabCouplingStep's Insert(0, ...) is the worst of them: it shifts every index, so DropOldestTempTab can remove the wrong tab between its index lookup and RemoveAt. Locks sit around the individual mutations, never around a Draw call, so no step holds the lock across rendering.
This commit is contained in:
@@ -69,7 +69,10 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep
|
|||||||
// Insert a victim at index 0: a regressed index-0 getter would return
|
// Insert a victim at index 0: a regressed index-0 getter would return
|
||||||
// THIS instead of ActiveTab, so ReferenceEquals would catch it.
|
// THIS instead of ActiveTab, so ReferenceEquals would catch it.
|
||||||
var victim = new Tab { Name = "selftest-coupling-victim" };
|
var victim = new Tab { Name = "selftest-coupling-victim" };
|
||||||
Plugin.Config.Tabs.Insert(0, victim);
|
// Insert shifts every index; the worker's DropOldestTempTab holds
|
||||||
|
// TabsListLock across its index lookup and removal.
|
||||||
|
lock (_plugin.TabsListLock)
|
||||||
|
Plugin.Config.Tabs.Insert(0, victim);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!ReferenceEquals(_plugin.CurrentTab, _plugin.MainWindow.ActiveTab))
|
if (!ReferenceEquals(_plugin.CurrentTab, _plugin.MainWindow.ActiveTab))
|
||||||
@@ -97,7 +100,8 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Plugin.Config.Tabs.Remove(victim);
|
lock (_plugin.TabsListLock)
|
||||||
|
Plugin.Config.Tabs.Remove(victim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep
|
|||||||
[ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All),
|
[ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Plugin.Config.Tabs.Add(injected);
|
// Config.Tabs is mutated by the message worker under TabsListLock; a
|
||||||
|
// framework-thread writer must take the same lock.
|
||||||
|
lock (plugin.TabsListLock)
|
||||||
|
Plugin.Config.Tabs.Add(injected);
|
||||||
Tab? active = null;
|
Tab? active = null;
|
||||||
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
|
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
|
||||||
try
|
try
|
||||||
@@ -91,7 +94,8 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Plugin.Config.Tabs.Remove(injected);
|
lock (plugin.TabsListLock)
|
||||||
|
Plugin.Config.Tabs.Remove(injected);
|
||||||
Plugin.Config.AutoTellTabsShowGreetedToggle = savedFlag;
|
Plugin.Config.AutoTellTabsShowGreetedToggle = savedFlag;
|
||||||
Plugin.Config.SidebarWidth = savedSidebarWidth;
|
Plugin.Config.SidebarWidth = savedSidebarWidth;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,13 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep
|
|||||||
}
|
}
|
||||||
injected.Add(BuildTempProbe("Tell Probe@SelfTest", pinned: false));
|
injected.Add(BuildTempProbe("Tell Probe@SelfTest", pinned: false));
|
||||||
injected.Add(BuildTempProbe("Pinned Probe@SelfTest", pinned: true));
|
injected.Add(BuildTempProbe("Pinned Probe@SelfTest", pinned: true));
|
||||||
foreach (var tab in injected)
|
// Config.Tabs is mutated by the message worker under TabsListLock; a
|
||||||
Plugin.Config.Tabs.Add(tab);
|
// framework-thread writer must take the same lock.
|
||||||
|
lock (plugin.TabsListLock)
|
||||||
|
{
|
||||||
|
foreach (var tab in injected)
|
||||||
|
Plugin.Config.Tabs.Add(tab);
|
||||||
|
}
|
||||||
|
|
||||||
Tab? active = null;
|
Tab? active = null;
|
||||||
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
|
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
|
||||||
@@ -84,8 +89,11 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
foreach (var tab in injected)
|
lock (plugin.TabsListLock)
|
||||||
Plugin.Config.Tabs.Remove(tab);
|
{
|
||||||
|
foreach (var tab in injected)
|
||||||
|
Plugin.Config.Tabs.Remove(tab);
|
||||||
|
}
|
||||||
Plugin.Config.AutoTellTabsCompactDisplay = savedCompact;
|
Plugin.Config.AutoTellTabsCompactDisplay = savedCompact;
|
||||||
Plugin.Config.SidebarWidth = savedSidebarWidth;
|
Plugin.Config.SidebarWidth = savedSidebarWidth;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user