Skip to content

Commit 3d97212

Browse files
authored
fix(stream): wrap close done ch with sync.Once (aws#2537)
Today, `stream.Stream` is structured as: ```go case <-streamer.Done(): // call Close() case <-time.After(fetchDelay): // call Fetch() ``` The problem is that it's possible that the `Done()` event is triggered at the same time as `time.After(fetchDelay)` and in that scenario the behavior is undeterministic. It's possible that we call `Fetch` a second time. For the ECS deployment streamer, this means that we would previous close the `s.done` channel twice. Wrapping the call with a `sync.Once` removes this concern. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent 4212955 commit 3d97212

File tree

1 file changed

+7
-1
lines changed

1 file changed

+7
-1
lines changed

internal/pkg/stream/ecs.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type ECSDeploymentStreamer struct {
7474
deploymentCreationTime time.Time
7575

7676
subscribers []chan ECSService
77+
once sync.Once
7778
done chan struct{}
7879
isDone bool
7980
pastEventIDs map[string]bool
@@ -143,7 +144,12 @@ func (s *ECSDeploymentStreamer) Fetch() (next time.Time, err error) {
143144
deployments = append(deployments, rollingDeploy)
144145
if isDeploymentDone(rollingDeploy, s.deploymentCreationTime) {
145146
// The deployment is done, notify that there is no need for another Fetch call beyond this point.
146-
close(s.done)
147+
// In stream.Stream, it's possible that both the <-Done() event is available as well as another Fetch()
148+
// call. In order to guarantee that we don't try to close the same stream multiple times, we wrap it with a
149+
// sync.Once.
150+
s.once.Do(func() {
151+
close(s.done)
152+
})
147153
}
148154
}
149155
var failureMsgs []string

0 commit comments

Comments
 (0)