Merge pull request #3532 from nspcc-dev/fix/ws-client-subscription-panic

rpcclient/WS: fix data race on concurrent (un)subscription
This commit is contained in:
Roman Khimov 2024-07-26 19:29:38 +03:00 committed by GitHub
commit c6ed92a169
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -960,6 +960,14 @@ func (c *WSClient) UnsubscribeAll() error {
// after WS RPC unsubscription request is completed. Until then the subscriber channel
// may still receive WS notifications.
func (c *WSClient) performUnsubscription(id string) error {
c.subscriptionsLock.RLock()
rcvrWas, ok := c.subscriptions[id]
c.subscriptionsLock.RUnlock()
if !ok {
return errors.New("no subscription with this ID")
}
var resp bool
if err := c.performRequest("unsubscribe", []any{id}, &resp); err != nil {
return err
@ -975,6 +983,15 @@ func (c *WSClient) performUnsubscription(id string) error {
if !ok {
return errors.New("no subscription with this ID")
}
cleanUpSubscriptions := true
if rcvrWas.Receiver() != rcvr.Receiver() {
// concurrent subscription has been done and been overwritten; this
// is not this routine's subscription, cleanup only receivers map
rcvr = rcvrWas
cleanUpSubscriptions = false
}
ch := rcvr.Receiver()
ids := c.receivers[ch]
for i, rcvrID := range ids {
@ -988,7 +1005,9 @@ func (c *WSClient) performUnsubscription(id string) error {
} else {
c.receivers[ch] = ids
}
delete(c.subscriptions, id)
if cleanUpSubscriptions {
delete(c.subscriptions, id)
}
return nil
}