Snapshot channel tree maps under client lock

This commit is contained in:
Brandon McGinty (chatgpt)
2026-08-09 18:15:44 -04:00
committed by Brandon McGinty
parent 4fe32ede70
commit f629ce0359
2 changed files with 36 additions and 15 deletions
+11 -12
View File
@@ -89,15 +89,12 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem {
var treeItem TreeItem
if ti, ok := item.(TreeItem); !ok {
root := b.Client.Channels[0]
var root *gumble.Channel
b.Client.Do(func() { root = b.Client.Channels[0] })
if root == nil {
return nil
}
return []uiterm.TreeItem{
TreeItem{
Channel: root,
},
}
return []uiterm.TreeItem{TreeItem{Channel: root}}
} else {
treeItem = ti
}
@@ -108,11 +105,17 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem {
users := []uiterm.TreeItem{}
ul := []*gumble.User{}
cl := []*gumble.Channel{}
// TCP handlers mutate both maps; snapshot them while Client.Do holds its
// read lock, then sort/render outside the protocol critical section.
b.Client.Do(func() {
for _, user := range treeItem.Channel.Users {
ul = append(ul, user)
var u = ul[len(ul)-1]
_ = u
}
for _, subchannel := range treeItem.Channel.Children {
cl = append(cl, subchannel)
}
})
sort.Slice(ul, func(i, j int) bool {
return ul[i].Name < ul[j].Name
})
@@ -123,10 +126,6 @@ func (b *Barnard) TreeItemBuild(item uiterm.TreeItem) []uiterm.TreeItem {
}
channels := []uiterm.TreeItem{}
cl := []*gumble.Channel{}
for _, subchannel := range treeItem.Channel.Children {
cl = append(cl, subchannel)
}
sort.Slice(cl, func(i, j int) bool {
return cl[i].Name < cl[j].Name
})
+22
View File
@@ -0,0 +1,22 @@
package main
import (
"testing"
"git.stormux.org/storm/barnard/gumble/gumble"
)
// Regression: rebuilding the channel tree ranged protocol-owned maps without
// Client.Do while TCP handlers could add or remove users/channels.
func TestTreeItemBuildReadsMapsUnderClientSnapshot(t *testing.T) {
root := &gumble.Channel{ID: 0, Users: gumble.Users{}, Children: gumble.Channels{}}
user := &gumble.User{Session: 1, Name: "user"}
child := &gumble.Channel{ID: 2, Name: "child", Users: gumble.Users{}, Children: gumble.Channels{}}
root.Users[user.Session] = user
root.Children[child.ID] = child
b := &Barnard{Client: &gumble.Client{Channels: gumble.Channels{0: root}}, MutedChannels: map[uint32]bool{}}
items := b.TreeItemBuild(TreeItem{Channel: root})
if len(items) != 2 {
t.Fatalf("got %d items", len(items))
}
}