fix(envd): fix data race in fan-out when subscriber is removed mid-iteration#2643
fix(envd): fix data race in fan-out when subscriber is removed mid-iteration#2643arkamar wants to merge 3 commits into
Conversation
❌ 10 Tests Failed:
View the full list of 11 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8b9a572a6d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Code Review
This pull request addresses a race condition in the MultiplexedChannel by creating a shallow copy of the subscribers slice before iteration, which prevents slice corruption during concurrent modifications. A regression test has been added to verify the fix and ensure stable delivery to subscribers. I have no further feedback.
8b9a572 to
17fdd0a
Compare
PR SummaryMedium Risk Overview Reviewed by Cursor Bugbot for commit 340d4c1. Bugbot is set up for automated code reviews on this repo. Configure here. |
…eration run() copied only the slice header under RLock, sharing the backing array with remove(). When remove() shifted elements in-place via append(channels[:i], channels[i+1:]...), the fan-out's stale snapshot would skip one subscriber and deliver to another twice — silent stdout/stderr corruption. Deep-copy the slice under RLock so the iteration is immune to concurrent mutations. Fixes: a67f983 ("fix(envd): fix fan-out deadlock when process subscriber disconnects (#2579)")
40f5326 to
8756a4c
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8756a4c. Configure here.
| for v := range m.Source { | ||
| m.mu.RLock() | ||
| subs := m.channels | ||
| subs := append([]*subscriber[T](nil), m.channels...) |
There was a problem hiding this comment.
| subs := append([]*subscriber[T](nil), m.channels...) | |
| subs := slices.Clone(m.channels) |
| @@ -54,7 +54,7 @@ func NewMultiplexedChannel[T any](buffer int) *MultiplexedChannel[T] { | |||
| func (m *MultiplexedChannel[T]) run() { | |||
| for v := range m.Source { | |||
There was a problem hiding this comment.
we should be able to move the allocation only to remove instead

run() copied only the slice header under RLock, sharing the backing array with remove(). When remove() shifted elements in-place via append(channels[:i], channels[i+1:]...), the fan-out's stale snapshot would skip one subscriber and deliver to another twice — silent stdout/stderr corruption. Deep-copy the slice under RLock so the iteration is immune to concurrent mutations.
Fixes: a67f983 ("fix(envd): fix fan-out deadlock when process subscriber disconnects (#2579)")