Skip to content

Commit 497797d

Browse files
authored
Removing need for runner.New (#1)
Removing need for runner.New()
1 parent 030ed69 commit 497797d

7 files changed

Lines changed: 97 additions & 54 deletions

File tree

README.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,44 @@ Documentation available on [GoDoc](https://godoc.org/github.com/raksly/runner).
1111
## Examples
1212
### Minimalistic
1313
```golang
14-
runner := runner.New(context.Background())
14+
var r runner.Runner
1515

16-
runner.Run(runHTTP)
17-
runner.Run(runTCP)
16+
r.Run(runHTTP)
17+
r.Run(runTCP)
1818

19-
runner.Wait()
19+
r.Wait()
2020
```
21-
`runHTTP` and `runTCP` are both of type `func()`. `runner.Run` will run both functions in separate goroutines, and `runner.Wait()` waits until both functions exit.
21+
`runHTTP` and `runTCP` are both of type `func()`. `r.Run` will run both functions in separate goroutines, and `r.Wait()` waits until both functions exit.
2222
### Exit notification
2323
When `runTCP` exits, it might be because the application is supposed to exit alltogether, or there was an irrecoverable error. In that case, you might want HTTP to exit aswell. `Run*` methods return a channel which is closed when its running function returns.
2424
```golang
2525
ctx, cancel := context.WithCancel(context.Background())
26-
runner := runner.New(ctx)
26+
r := runner.Runner{Ctx: ctx}
2727

2828
select {
29-
case <-runner.RunContext(runHTTP):
30-
case <-runner.RunContext(runTCP):
29+
case <-r.RunContext(runHTTP):
30+
case <-r.RunContext(runTCP):
3131
}
3232

3333
cancel()
34-
runner.Wait()
34+
r.Wait()
3535
```
3636
Both `runHTTP` and `runTCP` are now of type `func(context.Context)` and
37-
are given the context passed to `runner.New`. If either `runHTTP` or `runTCP` returns, `select` will break, the context will be cancelled, making the other function exit aswell in due time, and `runner.Wait()` waits for that.
37+
are given the context `Runner.Ctx`. If either `runHTTP` or `runTCP` returns, `select` will break, the context will be cancelled, making the other function exit aswell in due time, and `r.Wait()` waits for that.
3838
### Signals
3939
`Runner` contains a convenience method to work with OS signals
4040
```golang
4141
ctx, cancel := context.WithCancel(context.Background())
42-
runner := runner.New(ctx)
42+
r := runner.Runner{Ctx: ctx}
4343

4444
select {
45-
case <-runner.RunContext(runHTTP):
46-
case <-runner.RunContext(runTCP):
47-
case <-runner.RunSigs(syscall.SIGINT, syscall.SIGTERM):
45+
case <-r.RunContext(runHTTP):
46+
case <-r.RunContext(runTCP):
47+
case <-r.RunSigs(syscall.SIGINT, syscall.SIGTERM):
4848
}
4949

5050
cancel()
51-
runner.Wait()
51+
r.Wait()
5252
```
5353
If either `runHTTP` or `runTCP` returns, or `SIGINT`/`SIGTERM` is received,
5454
the context will be cancelled and we wait for everything to clean up.
@@ -57,16 +57,15 @@ You might want to pass more than just a context to your function. To archive
5757
this, you may use closures
5858
```golang
5959
ctx, cancel := context.WithCancel(context.Background())
60-
runner := runner.New(ctx)
60+
r := runner.Runner{Ctx: ctx}
6161

6262
select {
63-
// runSomething blocks on <-ctx.Done()
64-
case <-runner.Run(func() { runSomething(ctx, 1, 2, 3) }):
65-
case <-runner.RunSigs(syscall.SIGINT, syscall.SIGTERM):
66-
cancel()
63+
case <-r.Run(func() { runSomething(ctx, 1, 2, 3) }):
64+
case <-r.RunSigs(syscall.SIGINT, syscall.SIGTERM):
6765
}
6866

69-
runner.Wait()
67+
cancel()
68+
r.Wait()
7069
```
7170
## License
7271
MIT

examples/exit_notification/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ func runHTTP(ctx context.Context) {
2222

2323
func main() {
2424
ctx, cancel := context.WithCancel(context.Background())
25-
runner := runner.New(ctx)
25+
r := runner.Runner{Ctx: ctx}
2626

2727
select {
28-
case <-runner.RunContext(runHTTP):
28+
case <-r.RunContext(runHTTP):
2929
fmt.Println("Exited runHTTP")
30-
case <-runner.RunContext(runTCP):
30+
case <-r.RunContext(runTCP):
3131
fmt.Println("Exited runTCP")
3232
}
3333

3434
cancel()
35-
runner.Wait()
35+
r.Wait()
3636
}

examples/minimalistic/main.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"time"
76

@@ -21,10 +20,10 @@ func runHTTP() {
2120
}
2221

2322
func main() {
24-
runner := runner.New(context.Background())
23+
var r runner.Runner
2524

26-
runner.Run(runHTTP)
27-
runner.Run(runTCP)
25+
r.Run(runHTTP)
26+
r.Run(runTCP)
2827

29-
runner.Wait()
28+
r.Wait()
3029
}

examples/multiple_parameters/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ func runSomething(ctx context.Context, a, b, c int) {
1616

1717
func main() {
1818
ctx, cancel := context.WithCancel(context.Background())
19-
runner := runner.New(ctx)
19+
r := runner.Runner{Ctx: ctx}
2020

2121
select {
22-
case <-runner.Run(func() { runSomething(ctx, 1, 2, 3) }):
22+
case <-r.Run(func() { runSomething(ctx, 1, 2, 3) }):
2323
fmt.Println("Exited runSomething")
24-
case sig := <-runner.RunSigs(syscall.SIGINT, syscall.SIGTERM):
24+
case sig := <-r.RunSigs(syscall.SIGINT, syscall.SIGTERM):
2525
fmt.Println("Received signal", sig)
26-
cancel()
2726
}
2827

29-
runner.Wait()
28+
cancel()
29+
r.Wait()
3030
}

examples/signals/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ func runHTTP(ctx context.Context) {
2222

2323
func main() {
2424
ctx, cancel := context.WithCancel(context.Background())
25-
runner := runner.New(ctx)
25+
r := runner.Runner{Ctx: ctx}
2626

2727
select {
28-
case <-runner.RunContext(runHTTP):
28+
case <-r.RunContext(runHTTP):
2929
fmt.Println("Exited runHTTP")
30-
case <-runner.RunContext(runTCP):
30+
case <-r.RunContext(runTCP):
3131
fmt.Println("Exited runTCP")
32-
case sig := <-runner.RunSigs(syscall.SIGINT, syscall.SIGTERM):
32+
case sig := <-r.RunSigs(syscall.SIGINT, syscall.SIGTERM):
3333
fmt.Println("Received signal", sig)
3434
}
3535

3636
cancel()
37-
runner.Wait()
37+
r.Wait()
3838
}

runner.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,24 @@ import (
1919
)
2020

2121
// New creates a new Runner
22+
// Deprecated: Use runner.Runner{}
2223
func New(ctx context.Context) Runner {
23-
return Runner{
24-
ctx: ctx,
25-
wg: &sync.WaitGroup{},
26-
}
24+
return Runner{Ctx: ctx}
2725
}
2826

2927
// Runner runs functions in goroutines with `Run`, `RunContext` and/or
3028
// `RunOtherContext`, returning a channel that is closed when the goroutine
3129
// exits.
3230
// You may wait on all goroutines to finish using `Wait`.
3331
type Runner struct {
34-
ctx context.Context
35-
wg *sync.WaitGroup
32+
// Ctx will be passed to functions called by `RunContext`
33+
Ctx context.Context
34+
wg sync.WaitGroup
3635
}
3736

3837
// Run runs a function of type func() in a new goroutine.
3938
// The returned channel is closed when f returns
40-
func (r Runner) Run(f func()) <-chan struct{} {
39+
func (r *Runner) Run(f func()) <-chan struct{} {
4140
done := make(chan struct{})
4241
r.wg.Add(1)
4342
go func() {
@@ -51,31 +50,32 @@ func (r Runner) Run(f func()) <-chan struct{} {
5150
}
5251

5352
// RunContext is like `Run`, but passes the context given to `New`.
54-
func (r Runner) RunContext(f func(context.Context)) <-chan struct{} {
55-
return r.RunOtherContext(r.ctx, f)
53+
func (r *Runner) RunContext(f func(context.Context)) <-chan struct{} {
54+
return r.RunOtherContext(r.Ctx, f)
5655
}
5756

5857
// RunOtherContext is like `RunContext`, except you may specify which
5958
// context to be passed to f.
60-
func (r Runner) RunOtherContext(ctx context.Context, f func(context.Context)) <-chan struct{} {
59+
func (r *Runner) RunOtherContext(ctx context.Context, f func(context.Context)) <-chan struct{} {
6160
return r.Run(func() { f(ctx) })
6261
}
6362

6463
// RunSigs is a convenience method to work with OS signals.
6564
// Unlike the other `Run*` functions, the channel returned
6665
// is not closed, but reads the received signal.
67-
func (r Runner) RunSigs(sigs ...os.Signal) <-chan os.Signal {
66+
func (r *Runner) RunSigs(sigs ...os.Signal) <-chan os.Signal {
6867
sig := make(chan os.Signal, 1)
6968
signal.Notify(sig, sigs...)
7069
return sig
7170
}
7271

7372
// Wait waits for all goroutines started by this runner.
74-
func (r Runner) Wait() {
73+
func (r *Runner) Wait() {
7574
r.wg.Wait()
7675
}
7776

7877
// Context returns the context given to `New`.
79-
func (r Runner) Context() context.Context {
80-
return r.ctx
78+
// Deprecated: Access Runner.Ctx directly.
79+
func (r *Runner) Context() context.Context {
80+
return r.Ctx
8181
}

runner_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/raksly/runner"
11+
1112
"github.com/stretchr/testify/assert"
1213
)
1314

@@ -32,6 +33,50 @@ func assertChan(assert *assert.Assertions, c interface{}, expectClosed bool) {
3233
}
3334
}
3435

36+
func Test2(t *testing.T) {
37+
assert := assert.New(t)
38+
39+
ctx, cancel := context.WithCancel(context.Background())
40+
r := runner.Runner{Ctx: ctx}
41+
42+
c1 := r.Run(run)
43+
c2 := r.RunContext(runContext)
44+
c3 := r.RunOtherContext(ctx, runContext)
45+
c4 := r.RunSigs(syscall.SIGINT)
46+
47+
select {
48+
case <-c1:
49+
case <-c2:
50+
assert.Fail("should not run")
51+
case <-c3:
52+
assert.Fail("should not run")
53+
case <-c4:
54+
assert.Fail("should not run")
55+
}
56+
57+
assertChan(assert, c1, true)
58+
assertChan(assert, c2, false)
59+
assertChan(assert, c3, false)
60+
assertChan(assert, c4, false)
61+
62+
cancel()
63+
r.Wait()
64+
65+
assertChan(assert, c2, true)
66+
assertChan(assert, c3, true)
67+
assertChan(assert, c4, false)
68+
69+
var r2 runner.Runner
70+
71+
r2.Run(func() {})
72+
r2.RunContext(func(ctx context.Context) {
73+
assert.Nil(ctx)
74+
})
75+
76+
r2.Wait()
77+
}
78+
79+
// Keeping Test1 to ensure the old api still works
3580
func Test1(t *testing.T) {
3681
assert := assert.New(t)
3782

0 commit comments

Comments
 (0)