Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 55bfb59

Browse files
cezarsashin-
authored andcommitted
plugin/localbinary: Exit output stream goroutines when plugin closes
This commit ensures that when a plugin instance is closed the goroutines responsible for streaming stdout and stderr of the called binary will also exit, preventing a goroutines leak. Before this commit these goroutines could stay blocked forever if Close() was called while the binary still had some pending output. I found this bug after debugging a real world goroutine leak, the goroutines dump would show thousands of goroutines at: ``` github.com/tsuru/tsuru/vendor/github.com/docker/machine/libmachine/drivers/plugin/localbinary.stream(0xc023013d80, 0xc0000aad20) /home/travis/gopath/src/github.com/tsuru/tsuru/vendor/github.com/docker/machine/libmachine/drivers/plugin/localbinary/plugin.go:177 +0x7c created by github.com/tsuru/tsuru/vendor/github.com/docker/machine/libmachine/drivers/plugin/localbinary.(*Plugin).AttachStream /home/travis/gopath/src/github.com/tsuru/tsuru/vendor/github.com/docker/machine/libmachine/drivers/plugin/localbinary/plugin.go:183 +0x67 ``` Signed-off-by: Cezar Sa Espinola <cezarsa@gmail.com>
1 parent 5a8ce1a commit 55bfb59

File tree

2 files changed

+14
-10
lines changed

2 files changed

+14
-10
lines changed

‎libmachine/drivers/plugin/localbinary/plugin.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type Plugin struct {
7272
Addr string
7373
MachineName string
7474
addrCh chan string
75-
stopCh chan bool
75+
stopCh chan struct{}
7676
timeout time.Duration
7777
}
7878

@@ -121,7 +121,7 @@ func NewPlugin(driverName string) (*Plugin, error) {
121121
log.Debugf("Found binary path at %s", binaryPath)
122122

123123
return &Plugin{
124-
stopCh: make(chan bool),
124+
stopCh: make(chan struct{}),
125125
addrCh: make(chan string, 1),
126126
Executor: &Executor{
127127
DriverName: driverName,
@@ -168,19 +168,23 @@ func (lbe *Executor) Close() error {
168168
return nil
169169
}
170170

171-
func stream(scanner *bufio.Scanner, streamOutCh chan<- string) {
171+
func stream(scanner *bufio.Scanner, streamOutCh chan<- string, stopCh <-chan struct{}) {
172172
for scanner.Scan() {
173173
line := scanner.Text()
174174
if err := scanner.Err(); err != nil {
175175
log.Warnf("Scanning stream: %s", err)
176176
}
177-
streamOutCh <- strings.Trim(line, "\n")
177+
select {
178+
case streamOutCh <- strings.Trim(line, "\n"):
179+
case <-stopCh:
180+
return
181+
}
178182
}
179183
}
180184

181185
func (lbp *Plugin) AttachStream(scanner *bufio.Scanner) <-chan string {
182186
streamOutCh := make(chan string)
183-
go stream(scanner, streamOutCh)
187+
go stream(scanner, streamOutCh, lbp.stopCh)
184188
return streamOutCh
185189
}
186190

@@ -241,6 +245,6 @@ func (lbp *Plugin) Address() (string, error) {
241245
}
242246

243247
func (lbp *Plugin) Close() error {
244-
lbp.stopCh <- true
248+
close(lbp.stopCh)
245249
return nil
246250
}

‎libmachine/drivers/plugin/localbinary/plugin_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ func TestLocalBinaryPluginAddressTimeout(t *testing.T) {
7171

7272
func TestLocalBinaryPluginClose(t *testing.T) {
7373
lbp := &Plugin{}
74-
lbp.stopCh = make(chan bool, 1)
74+
lbp.stopCh = make(chan struct{})
7575
go lbp.Close()
76-
stopped := <-lbp.stopCh
77-
if !stopped {
76+
_, isOpen := <-lbp.stopCh
77+
if isOpen {
7878
t.Fatal("Close did not send a stop message on the proper channel")
7979
}
8080
}
@@ -106,7 +106,7 @@ func TestExecServer(t *testing.T) {
106106
MachineName: machineName,
107107
Executor: fe,
108108
addrCh: make(chan string, 1),
109-
stopCh: make(chan bool, 1),
109+
stopCh: make(chan struct{}),
110110
}
111111

112112
finalErr := make(chan error)

0 commit comments

Comments
 (0)