Reputation: 48
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
return
case <-intervalChan:
doSomething()
}
}
Hi,I write a func as above and want to know is it possible to cause goroutine leak.
For example, the first select statement sends a true to intervalChan, then the second select statement receives Done flag from ctx.Done() and return. Will the goroutine be block forever?
Upvotes: 0
Views: 450
Reputation: 109404
The only place your first goroutine could be blocked indefinitely is in intervalChan <- true
. Put it in another select block to be able to cancel that send:
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second * time.Duration(intervalTime)):
select {
case <-ctx.Done():
return
case intervalChan <- true:
}
}
}
}()
Upvotes: 1
Reputation: 2498
I cannot replicate this behaviour every time but could be some leak. If doSomething
do some heavy computation, meanwhile goroutine is blocked on intervalChan <- true
since it cannot push into the channel. After doSomething
finish execution and context was cancelled, startTimer exists before goroutine and this will lead into blocked goroutine, because there isn't any consumer of intervalChan
.
go version go1.8.3 darwin/amd64
package main
import (
"context"
"fmt"
"time"
)
func startTimer(ctx context.Context, intervalTime int) {
intervalChan := make(chan bool)
go func() {
for {
select {
case <-ctx.Done():
fmt.Println("Done from inside of goroutine.")
return
case <-time.After(time.Second * time.Duration(intervalTime)):
fmt.Println("Interval reached.")
intervalChan <- true
}
}
}()
for {
select {
case <-ctx.Done():
fmt.Println("Done from startTimer.")
return
case <-intervalChan:
time.Sleep(10 * time.Second)
fmt.Println("Done")
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
startTimer(ctx, 2)
}
Upvotes: 1