1 Commits

Author SHA1 Message Date
Y.Horie 76aa66015c fix(lbclient): prevent deadlock when all clients are removed (#2287)
LBClient.get() took an RLock and then read cs[0] without checking for an
empty slice. Once every client was removed via RemoveClients, cs is empty,
so get() panicked on cs[0]; because the RUnlock was not deferred it was
never reached, leaking the lock and deadlocking every later AddClient and
RemoveClients call.

Defer the unlock in get() and return nil for an empty client set, with
DoDeadline/DoTimeout reporting the new ErrNoAvailableClients instead of
panicking. AddClient and RemoveClients now also defer their unlock so a
panic in the user-supplied RemoveClients callback can't leak the write
lock.

RemoveClients additionally niled cc.cs[idx] before invoking rc, so a panic
in rc left cc.cs with nil holes that would later crash get(). Compact the
slice first and nil the unused tail only afterwards, so a panic leaves the
client set usable.

Fixes #2270

Signed-off-by: Y.Horie <u5.horie@gmail.com>
2026-06-13 09:24:36 +02:00