resources: aws: ec2: Fix deadlock on rare error scenarios

If we get an error in the Watch loop, it will send this on awsChan,
which will cause Watch to loop. However, in this scenario it will never
cause closeChan to close, and we will deadlock because we have a
waitGroup in a helper goroutine which is waiting on this channel to
close the context.

Normally this wouldn't be an issue, but since we have more than one
goroutine (with associated waitGroup) it is. It's also good practice to
close all the channels to help avoid this kind of bug.

This patch also moves the waitGroup Wait into a more logical place for
visibility.
This commit is contained in:
James Shubin
2017-11-10 14:14:27 -05:00
parent c8ddbeaa5c
commit 91a9edb322

View File

@@ -190,6 +190,8 @@ func (obj *AwsEc2Res) longpollWatch() error {
if err := obj.Running(); err != nil { if err := obj.Running(); err != nil {
return err return err
} }
defer obj.wg.Wait()
defer close(obj.closeChan)
ctx, cancel := context.WithCancel(context.TODO()) ctx, cancel := context.WithCancel(context.TODO())
obj.wg.Add(1) obj.wg.Add(1)
go func() { go func() {
@@ -200,7 +202,6 @@ func (obj *AwsEc2Res) longpollWatch() error {
} }
}() }()
obj.wg.Add(1) obj.wg.Add(1)
defer obj.wg.Wait()
go func() { go func() {
defer obj.wg.Done() defer obj.wg.Done()
defer close(obj.awsChan) defer close(obj.awsChan)
@@ -433,7 +434,6 @@ func (obj *AwsEc2Res) longpollWatch() error {
select { select {
case event := <-obj.Events(): case event := <-obj.Events():
if exit, send = obj.ReadEvent(event); exit != nil { if exit, send = obj.ReadEvent(event); exit != nil {
close(obj.closeChan)
return *exit return *exit
} }
case msg, ok := <-obj.awsChan: case msg, ok := <-obj.awsChan: