Use done channel to avoid goroutine leak

This deals with a memory leak, caused by goroutines, experienced when using the
s3 driver. Unfortunately, this section of the code leaks goroutines like a
sieve. There is probably some refactoring that could be done to avoid this but
instead, we have a done channel that will cause waiting goroutines to exit.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day 2015-04-28 14:06:24 -07:00 committed by Richard
parent a72fb20b85
commit f132ff7702

View file

@ -310,6 +310,7 @@ func (d *driver) WriteStream(path string, offset int64, reader io.Reader) (total
var putErrChan chan error var putErrChan chan error
parts := []s3.Part{} parts := []s3.Part{}
var part s3.Part var part s3.Part
done := make(chan struct{}) // stopgap to free up waiting goroutines
multi, err := d.Bucket.InitMulti(d.s3Path(path), d.getContentType(), getPermissions(), d.getOptions()) multi, err := d.Bucket.InitMulti(d.s3Path(path), d.getContentType(), getPermissions(), d.getOptions())
if err != nil { if err != nil {
@ -344,6 +345,7 @@ func (d *driver) WriteStream(path string, offset int64, reader io.Reader) (total
} }
d.putbuf(buf) // needs to be here to pick up new buf value d.putbuf(buf) // needs to be here to pick up new buf value
close(done) // free up any waiting goroutines
}() }()
// Fills from 0 to total from current // Fills from 0 to total from current
@ -407,7 +409,11 @@ func (d *driver) WriteStream(path string, offset int64, reader io.Reader) (total
// the underlying s3 library should handle it, it doesn't seem to // the underlying s3 library should handle it, it doesn't seem to
// be part of the shouldRetry function (see AdRoll/goamz/s3). // be part of the shouldRetry function (see AdRoll/goamz/s3).
defer func() { defer func() {
putErrChan <- nil // for some reason, we do this no matter what. select {
case putErrChan <- nil: // for some reason, we do this no matter what.
case <-done:
return // ensure we don't leak the goroutine
}
}() }()
if bytesRead <= 0 { if bytesRead <= 0 {
@ -449,7 +455,11 @@ func (d *driver) WriteStream(path string, offset int64, reader io.Reader) (total
if err != nil { if err != nil {
logrus.Errorf("error putting part, aborting: %v", err) logrus.Errorf("error putting part, aborting: %v", err)
putErrChan <- err select {
case putErrChan <- err:
case <-done:
return // don't leak the goroutine
}
} }
// parts and partNumber are safe, because this function is the // parts and partNumber are safe, because this function is the