Skip to content

Commit a2bb1cb

Browse files
authored
fix(envd): fix data race in fan-out when subscriber is removed mid-iteration (#2643)
run() snapshots the m.channels slice header under RLock and iterates after unlocking. remove() used append(channels[:i], channels[i+1:]...) which shifts the shared backing array in-place, corrupting the concurrent iteration — a subscriber can receive a value twice while another misses it entirely. Fix remove() to allocate a new backing array via slices.Concat so the old slice that run() holds remains stable. This moves the allocation to the cold path (subscriber disconnect) instead of the hot path (every fan-out delivery). Fixes: a67f983 ("fix(envd): fix fan-out deadlock when process subscriber disconnects (#2579)")
1 parent ab62536 commit a2bb1cb

2 files changed

Lines changed: 4 additions & 2 deletions

File tree

packages/envd/internal/services/process/handler/multiplex.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package handler
22

33
import (
4+
"slices"
45
"sync"
56
"sync/atomic"
67
)
@@ -128,7 +129,8 @@ func (m *MultiplexedChannel[T]) remove(s *subscriber[T]) {
128129

129130
for i, sub := range m.channels {
130131
if sub == s {
131-
m.channels = append(m.channels[:i], m.channels[i+1:]...)
132+
// New backing array so run()'s concurrent iteration is safe.
133+
m.channels = slices.Concat(m.channels[:i], m.channels[i+1:])
132134

133135
return
134136
}

packages/envd/pkg/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package pkg
22

3-
const Version = "0.5.17"
3+
const Version = "0.5.18"

0 commit comments

Comments
 (0)