Skip to content

Commit e391838

Browse files
committed
Fix memory leak caused by unclosed go-routine
1 parent f7002b5 commit e391838

File tree

5 files changed

+44
-66
lines changed

5 files changed

+44
-66
lines changed

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,30 @@ fmt.Println(c.Stdout())
2626
fmt.Println(c.Stderr())
2727
```
2828

29-
### Stream output to stderr and stdout
29+
### Configure the command
30+
31+
To configure the command a option function will be passed which receives the command object as an argument passed by reference.
32+
33+
Default option functions:
34+
35+
- `cmd.WithStandardStreams`
36+
- `cmd.WithTimeout(time.Duration)`
37+
38+
#### Example
3039

3140
```go
3241
c := cmd.NewCommand("echo hello", cmd.WithStandardStreams)
3342
c.Execute()
3443
```
3544

36-
### Set custom options
45+
#### Set custom options
3746

3847
```go
39-
func SetTimeout(c *Command) {
40-
c.Timeout = 1 * time.Hour
41-
}
42-
43-
func SetWorkingDir(c *Command) {
48+
setWorkingDir := func (c *Command) {
4449
c.WorkingDir = "/tmp/test"
4550
}
4651

47-
c := cmd.NewCommand("pwd", SetTimeout, SetWorkingDir)
52+
c := cmd.NewCommand("pwd", setWorkingDir)
4853
c.Execute()
4954
```
5055

command.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,25 @@ func NewCommand(cmd string, options ...func(*Command)) *Command {
6565
//
6666
// Example:
6767
//
68-
// c := cmd.NewCommand("echo hello", cmd.WithStandardStreams)
69-
// c.Execute()
68+
// c := cmd.NewCommand("echo hello", cmd.WithStandardStreams)
69+
// c.Execute()
7070
//
7171
func WithStandardStreams(c *Command) {
7272
c.StdoutWriter = os.Stdout
7373
c.StderrWriter = os.Stderr
7474
}
7575

76+
// WithTimeout sets the timeout of the command
77+
//
78+
// Example:
79+
// cmd.NewCommand("sleep 10;", cmd.WithTimeout(500))
80+
//
81+
func WithTimeout(t time.Duration) func(c *Command) {
82+
return func(c *Command) {
83+
c.Timeout = t
84+
}
85+
}
86+
7687
// AddEnv adds an environment variable to the command
7788
// If a variable gets passed like ${VAR_NAME} the env variable will be read out by the current shell
7889
func (c *Command) AddEnv(key string, value string) {
@@ -97,27 +108,6 @@ func parseEnvVariableFromShell(val string) []string {
97108
return matches
98109
}
99110

100-
//SetTimeoutMS sets the timeout in milliseconds
101-
func (c *Command) SetTimeoutMS(ms int) {
102-
if ms == 0 {
103-
c.Timeout = 1 * time.Minute
104-
return
105-
}
106-
c.Timeout = time.Duration(ms) * time.Millisecond
107-
}
108-
109-
// SetTimeout sets the timeout given a time unit
110-
// Example: SetTimeout("100s") sets the timeout to 100 seconds
111-
func (c *Command) SetTimeout(timeout string) error {
112-
d, err := time.ParseDuration(timeout)
113-
if err != nil {
114-
return err
115-
}
116-
117-
c.Timeout = d
118-
return nil
119-
}
120-
121111
//Stdout returns the output to stdout
122112
func (c *Command) Stdout() string {
123113
c.isExecuted("Stdout")
@@ -162,9 +152,18 @@ func (c *Command) Execute() error {
162152
return err
163153
}
164154

165-
done := make(chan error)
155+
done := make(chan error, 1)
156+
defer close(done)
157+
quit := make(chan bool, 1)
158+
defer close(quit)
159+
166160
go func() {
167-
done <- cmd.Wait()
161+
select {
162+
case <-quit:
163+
return
164+
case done <- cmd.Wait():
165+
return
166+
}
168167
}()
169168

170169
select {
@@ -175,6 +174,7 @@ func (c *Command) Execute() error {
175174
}
176175
c.exitCode = 0
177176
case <-time.After(c.Timeout):
177+
quit <- true
178178
if err := cmd.Process.Kill(); err != nil {
179179
return fmt.Errorf("Timeout occurred and can not kill process with pid %v", cmd.Process.Pid)
180180
}

command_linux_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"strings"
88
"testing"
9+
"time"
910
)
1011

1112
func TestCommand_ExecuteStderr(t *testing.T) {
@@ -18,8 +19,7 @@ func TestCommand_ExecuteStderr(t *testing.T) {
1819
}
1920

2021
func TestCommand_WithTimeout(t *testing.T) {
21-
cmd := NewCommand("sleep 0.01;")
22-
cmd.SetTimeoutMS(1)
22+
cmd := NewCommand("sleep 0.01;", WithTimeout(1*time.Millisecond))
2323

2424
err := cmd.Execute()
2525

@@ -30,8 +30,7 @@ func TestCommand_WithTimeout(t *testing.T) {
3030
}
3131

3232
func TestCommand_WithValidTimeout(t *testing.T) {
33-
cmd := NewCommand("sleep 0.01;")
34-
cmd.SetTimeoutMS(500)
33+
cmd := NewCommand("sleep 0.01;", WithTimeout(500*time.Millisecond))
3534

3635
err := cmd.Execute()
3736

command_test.go

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -108,31 +108,6 @@ func getCommand() string {
108108
return command
109109
}
110110

111-
func TestCommand_SetTimeoutMS_DefaultTimeout(t *testing.T) {
112-
c := NewCommand("echo test")
113-
c.SetTimeoutMS(0)
114-
assert.Equal(t, (1 * time.Minute), c.Timeout)
115-
}
116-
117-
func TestCommand_SetTimeoutMS(t *testing.T) {
118-
c := NewCommand("echo test")
119-
c.SetTimeoutMS(100)
120-
assert.Equal(t, 100*time.Millisecond, c.Timeout)
121-
}
122-
123-
func TestCommand_SetTimeout(t *testing.T) {
124-
c := NewCommand("echo test")
125-
_ = c.SetTimeout("100s")
126-
duration, _ := time.ParseDuration("100s")
127-
assert.Equal(t, duration, c.Timeout)
128-
}
129-
130-
func TestCommand_SetInvalidTimeout(t *testing.T) {
131-
c := NewCommand("echo test")
132-
err := c.SetTimeout("1")
133-
assert.Equal(t, "time: missing unit in duration 1", err.Error())
134-
}
135-
136111
func TestCommand_SetOptions(t *testing.T) {
137112
writer := &bytes.Buffer{}
138113

command_windows_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/stretchr/testify/assert"
55
"strings"
66
"testing"
7+
"time"
78
)
89

910
func TestCommand_ExecuteStderr(t *testing.T) {
@@ -16,8 +17,7 @@ func TestCommand_ExecuteStderr(t *testing.T) {
1617
}
1718

1819
func TestCommand_WithTimeout(t *testing.T) {
19-
cmd := NewCommand("timeout 0.005;")
20-
cmd.SetTimeoutMS(5)
20+
cmd := NewCommand("timeout 0.005;", WithTimeout(5*time.Millisecond))
2121

2222
err := cmd.Execute()
2323

@@ -28,8 +28,7 @@ func TestCommand_WithTimeout(t *testing.T) {
2828
}
2929

3030
func TestCommand_WithValidTimeout(t *testing.T) {
31-
cmd := NewCommand("timeout 0.01;")
32-
cmd.SetTimeoutMS(1000)
31+
cmd := NewCommand("timeout 0.01;", WithTimeout(1000*time.Millisecond))
3332

3433
err := cmd.Execute()
3534

0 commit comments

Comments
 (0)