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:
2026-08-17 06:49:42 +02:00
parent ce5973aea9
commit 99dca8cb31
3 changed files with 24 additions and 8 deletions
@@ -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;
}