Improve thread safety of resource map

This commit is contained in:
Bohdan Horbeshko 2026-08-09 14:16:20 -04:00
parent 4e932d1e26
commit 535423398a
2 changed files with 20 additions and 22 deletions

View file

@ -235,9 +235,10 @@ func (c *Client) Disconnect(resource string, quit bool) bool {
c.deleteResource(resource) c.deleteResource(resource)
} }
// other resources are still active // other resources are still active
if (len(c.resources) > 0 || c.Session.KeepOnline) && !quit { remaining := c.resourcesSnapshot()
log.Infof("Resource %v for account %v has disconnected, %v remaining", resource, c.Session.Login, len(c.resources)) if (len(remaining) > 0 || c.Session.KeepOnline) && !quit {
log.Debugf("Resources: %#v", c.resources) log.Infof("Resource %v for account %v has disconnected, %v remaining", resource, c.Session.Login, len(remaining))
log.Debugf("Resources: %#v", remaining)
return false return false
} }
// already disconnected // already disconnected

View file

@ -1928,7 +1928,7 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
sha1 := c.GetPhotoSha1(chat.Photo.Small, chatId) sha1 := c.GetPhotoSha1(chat.Photo.Small, chatId)
size := c.GetPhotoSize(chat.Photo.Small) size := c.GetPhotoSize(chat.Photo.Small)
for resource := range c.resourcesRange() { for _, resource := range c.resourcesSnapshot() {
features, ok := c.XmppClientFeatures[resource] features, ok := c.XmppClientFeatures[resource]
if ok && features != nil { if ok && features != nil {
for _, feature := range *features { for _, feature := range *features {
@ -2388,23 +2388,20 @@ func (c *Client) deleteResource(resource string) {
} }
} }
func (c *Client) resourcesRange() chan string { // resourcesSnapshot copies the currently known resources under the lock, so
// callers can safely iterate/send without holding resourcesLock for the
// whole operation (which used to block concurrent addResource/deleteResource
// for as long as a caller's loop body took, e.g. a whole batch of network
// sends).
func (c *Client) resourcesSnapshot() []string {
c.locks.resourcesLock.Lock() c.locks.resourcesLock.Lock()
defer c.locks.resourcesLock.Unlock()
resourceChan := make(chan string, 1) resources := make([]string, 0, len(c.resources))
go func() {
defer func() {
c.locks.resourcesLock.Unlock()
close(resourceChan)
}()
for resource := range c.resources { for resource := range c.resources {
resourceChan <- resource resources = append(resources, resource)
} }
}() return resources
return resourceChan
} }
// resend statuses to (to another resource, for example) // resend statuses to (to another resource, for example)
@ -2800,7 +2797,7 @@ func (c *Client) subscribeToID(id int64, chat *client.Chat, firstTime bool) {
if chat != nil { if chat != nil {
if c.Session.MUC && c.IsGroup(chat) { if c.Session.MUC && c.IsGroup(chat) {
if firstTime { if firstTime {
for resource := range c.resourcesRange() { for _, resource := range c.resourcesSnapshot() {
gateway.InviteToMUC(id, c.jid+"/"+resource, c.xmpp) gateway.InviteToMUC(id, c.jid+"/"+resource, c.xmpp)
} }
} }
@ -2933,7 +2930,7 @@ func (c *Client) getFromOutbox(xmppId string) string {
func (c *Client) GetCarbonFullJids(isOutgoing bool, ignoredResource string, forceFull bool) []string { func (c *Client) GetCarbonFullJids(isOutgoing bool, ignoredResource string, forceFull bool) []string {
var jids []string var jids []string
if isOutgoing { if isOutgoing {
for resource := range c.resourcesRange() { for _, resource := range c.resourcesSnapshot() {
if ignoredResource == "" || resource != ignoredResource { if ignoredResource == "" || resource != ignoredResource {
jids = append(jids, c.jid+"/"+resource) jids = append(jids, c.jid+"/"+resource)
} }
@ -3317,7 +3314,7 @@ func (c *Client) MigrateToMUCs() {
for _, chatID := range chatIDs { for _, chatID := range chatIDs {
c.unsubscribe(chatID) c.unsubscribe(chatID)
for resource := range c.resourcesRange() { for _, resource := range c.resourcesSnapshot() {
gateway.InviteToMUC(chatID, c.jid+"/"+resource, c.xmpp) gateway.InviteToMUC(chatID, c.jid+"/"+resource, c.xmpp)
} }
} }