diff --git a/HellionChat/SelfTests/CurrentTabCouplingStep.cs b/HellionChat/SelfTests/CurrentTabCouplingStep.cs index a6e3ddc..aa36c7e 100644 --- a/HellionChat/SelfTests/CurrentTabCouplingStep.cs +++ b/HellionChat/SelfTests/CurrentTabCouplingStep.cs @@ -69,7 +69,10 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep // Insert a victim at index 0: a regressed index-0 getter would return // THIS instead of ActiveTab, so ReferenceEquals would catch it. 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 { if (!ReferenceEquals(_plugin.CurrentTab, _plugin.MainWindow.ActiveTab)) @@ -97,7 +100,8 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep } finally { - Plugin.Config.Tabs.Remove(victim); + lock (_plugin.TabsListLock) + Plugin.Config.Tabs.Remove(victim); } } finally diff --git a/HellionChat/SelfTests/SidebarGreetedGlyphStep.cs b/HellionChat/SelfTests/SidebarGreetedGlyphStep.cs index 3687ee6..c87b875 100644 --- a/HellionChat/SelfTests/SidebarGreetedGlyphStep.cs +++ b/HellionChat/SelfTests/SidebarGreetedGlyphStep.cs @@ -49,7 +49,10 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep [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; var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded try @@ -91,7 +94,8 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep } finally { - Plugin.Config.Tabs.Remove(injected); + lock (plugin.TabsListLock) + Plugin.Config.Tabs.Remove(injected); Plugin.Config.AutoTellTabsShowGreetedToggle = savedFlag; Plugin.Config.SidebarWidth = savedSidebarWidth; } diff --git a/HellionChat/SelfTests/SidebarSectionHeaderStep.cs b/HellionChat/SelfTests/SidebarSectionHeaderStep.cs index dbaedd3..1915ebd 100644 --- a/HellionChat/SelfTests/SidebarSectionHeaderStep.cs +++ b/HellionChat/SelfTests/SidebarSectionHeaderStep.cs @@ -50,8 +50,13 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep } injected.Add(BuildTempProbe("Tell Probe@SelfTest", pinned: false)); injected.Add(BuildTempProbe("Pinned Probe@SelfTest", pinned: true)); - foreach (var tab in injected) - Plugin.Config.Tabs.Add(tab); + // Config.Tabs is mutated by the message worker under TabsListLock; a + // framework-thread writer must take the same lock. + lock (plugin.TabsListLock) + { + foreach (var tab in injected) + Plugin.Config.Tabs.Add(tab); + } Tab? active = null; var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded @@ -84,8 +89,11 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep } finally { - foreach (var tab in injected) - Plugin.Config.Tabs.Remove(tab); + lock (plugin.TabsListLock) + { + foreach (var tab in injected) + Plugin.Config.Tabs.Remove(tab); + } Plugin.Config.AutoTellTabsCompactDisplay = savedCompact; Plugin.Config.SidebarWidth = savedSidebarWidth; }