Skip to content

Commit dc1211d

Browse files
authored
Merge pull request #463 from devlights/add-goroutine-leak-examples
2 parents d77c886 + bfb7975 commit dc1211d

File tree

8 files changed

+244
-1
lines changed

8 files changed

+244
-1
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ clean:
5353
install:
5454
$(GOINSTALL) $(BIN_DIR)
5555

56+
.PHONY: vet
57+
vet:
58+
go vet ./...
59+
staticcheck ./...
60+
5661
.PHONY: run
5762
run:
5863
$(GORUN) $(CMD_PKG) -onetime -example ${EXAMPLE}

examples/basic/goroutines/examples.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package goroutines
22

3-
import "github.com/devlights/try-golang/mapping"
3+
import (
4+
"github.com/devlights/try-golang/examples/basic/goroutines/leak"
5+
"github.com/devlights/try-golang/mapping"
6+
)
47

58
type (
69
register struct{}
@@ -23,4 +26,6 @@ func (r *register) Regist(m mapping.ExampleMapping) {
2326
m["goroutines_using_chan_semaphore"] = UsingChanSemaphore
2427
m["goroutines_using_mutex"] = UsingMutex
2528
m["goroutines_with_context_deadline"] = WithContextDeadline
29+
30+
leak.NewRegister().Regist(m)
2631
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package leak
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/devlights/gomy/output"
8+
)
9+
10+
// AbandonedReceiver -- goroutineリークが発生するパターンのサンプルです。
11+
//
12+
// チャネルの送受信の実装があるが、タイミングによっては送信側がいなくなってしまうパターン。
13+
// 受信側のgoroutineが永遠に待ち続けるので終了しません。
14+
//
15+
// 解決方法としては、送信側が適切に使い終わったチャネルを閉じること。
16+
//
17+
// REFERENCES:
18+
// - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
19+
func AbandonedReceiver() error {
20+
var (
21+
ctx, cxl = context.WithTimeout(context.Background(), 10*time.Millisecond)
22+
ch = make(chan int)
23+
iowait = func() {
24+
time.Sleep(1 * time.Second)
25+
}
26+
fn = func(ch <-chan int) {
27+
iowait()
28+
data := <-ch
29+
output.Stdoutl("[recv]", data)
30+
}
31+
)
32+
defer cxl()
33+
34+
go fn(ch)
35+
36+
select {
37+
case ch <- 1:
38+
output.Stdoutl("[send]", 1)
39+
case <-ctx.Done():
40+
}
41+
42+
//
43+
// チャネルにデータを送信するものがいなくなるので
44+
// 上のgoroutineはプロセスが起動中は永遠に終了しません。
45+
//
46+
time.Sleep(1 * time.Second)
47+
48+
return nil
49+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package leak
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/devlights/gomy/output"
8+
)
9+
10+
// AbandonedSender -- goroutineリークが発生するパターンのサンプルです。
11+
//
12+
// チャネルの送受信の実装があるが、タイミングによっては受信側がいなくなってしまうパターン。
13+
// 送信側のgoroutineが永遠に待ち続けるので終了しません。
14+
//
15+
// 解決方法としては、Bufferedなチャネルを使うこと。
16+
//
17+
// REFERENCES:
18+
// - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
19+
func AbandonedSender() error {
20+
var (
21+
ctx, cxl = context.WithTimeout(context.Background(), 10*time.Millisecond)
22+
ch = make(chan int)
23+
iowait = func() {
24+
time.Sleep(1 * time.Second)
25+
}
26+
fn = func(ch chan<- int) {
27+
iowait()
28+
ch <- 1
29+
output.Stdoutl("[send]", 1)
30+
}
31+
)
32+
defer cxl()
33+
34+
go fn(ch)
35+
select {
36+
case v := <-ch:
37+
output.Stdoutl("[recv]", v)
38+
case <-ctx.Done():
39+
}
40+
41+
//
42+
// チャネルからデータを受信するものがいなくなるので
43+
// 上のgoroutineはプロセスが起動中は永遠に終了しません。
44+
//
45+
time.Sleep(1 * time.Second)
46+
47+
return nil
48+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package leak
2+
3+
import "github.com/devlights/try-golang/mapping"
4+
5+
type (
6+
register struct{}
7+
)
8+
9+
// NewRegister -- このパッケージ用のサンプルを登録する mapping.Register を生成します。
10+
func NewRegister() mapping.Register {
11+
return new(register)
12+
}
13+
14+
// Regist -- 登録します.
15+
func (r *register) Regist(m mapping.ExampleMapping) {
16+
m["goroutines_leak_forgotten_sender"] = ForgottenSender
17+
m["goroutines_leak_forgotten_receiver"] = ForgottenReceiver
18+
m["goroutines_leak_abandoned_sender"] = AbandonedSender
19+
m["goroutines_leak_abandoned_receiver"] = AbandonedReceiver
20+
m["goroutines_leak_sender_after_error_check"] = SenderAfterErrorCheck
21+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package leak
2+
3+
import (
4+
"time"
5+
6+
"github.com/devlights/gomy/output"
7+
)
8+
9+
// ForgottenReceiver -- goroutineリークが発生するパターンのサンプルです。
10+
//
11+
// チャネルを作成し、チャネルの受信側がいないパターン。
12+
// 送信側のgoroutineが永遠に待ち続けるので終了しません。
13+
//
14+
// 解決方法としては、Bufferedなチャネルを使うこと。
15+
//
16+
// REFERENCES:
17+
// - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
18+
func ForgottenReceiver() error {
19+
var (
20+
fn = func(ch chan<- int) {
21+
ch <- 1
22+
output.Stdoutl("[goroutine leak]", 1)
23+
}
24+
ch = make(chan int)
25+
)
26+
27+
go fn(ch)
28+
29+
//
30+
// チャネルのデータを受信するものがいないので
31+
// 上のgoroutineはプロセスが起動中は永遠に終了しません。
32+
//
33+
time.Sleep(1 * time.Second)
34+
35+
return nil
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package leak
2+
3+
import (
4+
"time"
5+
6+
"github.com/devlights/gomy/output"
7+
)
8+
9+
// ForgottenSender -- goroutineリークが発生するパターンのサンプルです。
10+
//
11+
// チャネルを作成し、チャネルの送信側がいないパターン。
12+
// 受信側のgoroutineが永遠に待ち続けるので終了しません。
13+
//
14+
// 解決方法としては、送信側が適切に使い終わったチャネルを閉じること。
15+
//
16+
// REFERENCES:
17+
// - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
18+
func ForgottenSender() error {
19+
var (
20+
fn = func(ch <-chan int) {
21+
data := <-ch
22+
output.Stdoutl("[goroutine leak]", data)
23+
}
24+
ch = make(chan int)
25+
)
26+
27+
go fn(ch)
28+
29+
//
30+
// チャネルにデータを送信するものがいないので
31+
// 上のgoroutineはプロセスが起動中は永遠に終了しません。
32+
//
33+
time.Sleep(1 * time.Second)
34+
35+
return nil
36+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package leak
2+
3+
import (
4+
"errors"
5+
6+
"github.com/devlights/gomy/output"
7+
)
8+
9+
// SenderAfterErrorCheck -- goroutineリークが発生するパターンのサンプルです。
10+
//
11+
// チャネルの送受信の実装があるが、内部の処理結果によっては送信側がいなくなってしまうパターン。
12+
// 受信側のgoroutineが永遠に待ち続けるので終了しません。
13+
//
14+
// 解決方法としては、送信側が適切に使い終わったチャネルを閉じること。
15+
//
16+
// REFERENCES:
17+
// - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
18+
func SenderAfterErrorCheck() error {
19+
var (
20+
ch = make(chan int)
21+
proc = func() bool {
22+
return false
23+
}
24+
fn = func(ch <-chan int) {
25+
data := <-ch
26+
output.Stdoutl("[recv]", data)
27+
}
28+
)
29+
30+
go fn(ch)
31+
32+
if !proc() {
33+
return errors.New("this is dummy error")
34+
}
35+
36+
//
37+
// 上でエラーが発生した場合、以下は処理されない。
38+
// なので、上で起動しているgoroutineは永遠に受信待機することになる。
39+
//
40+
ch <- 1
41+
42+
return nil
43+
}

0 commit comments

Comments
 (0)