context cancel does not exit - go

Expected: To be done after approx. 2 seconds
Actual: Runs indefinitely.
Don't understand what could be causing it to run indefinitely.
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for i := range generator(ctx) {
select {
case <-time.After(2 * time.Second):
cancel()
return
default:
fmt.Println(i)
}
}
}
func generator(ctx context.Context) <-chan int {
ch := make(chan int)
go func() {
count := 0
for {
select {
case <-ctx.Done():
return
case ch <- count:
count++
}
}
}()
return ch
}

The main issue is that your channel returned from generator(ctx) emits values almost as fast as you can read them.
The channel created by time.After(2 * time.Second) is discarded almost immediately, and you create a new timeout channel every iteration through the generator.
If you make one small change; create the timeout channel outside the loop, and then put it in the select clause you'll see it begin to work.
timeout := time.After(2 * time.Second)
for i := range generator(ctx) {
select {
case <-timeout:
cancel()
return
default:
fmt.Println(i)
}
}
https://play.golang.org/p/zb3wn5FJuK

Related

Golang infinite-loop timeout

I am trying to read a constant stream of data, if the call to receive stream takes longer than 30 seconds I need to timeout and exit the program. I am not sure how to exit the go routine once a timeout has been received.
func ReceiveStreamMessages(strm Stream, msg chan<- []byte) error {
d := make(chan []byte, 1)
e := make(chan error)
tm := time.After(30 * time.Second)
go func() {
for {
//blocking call
data, err := strm.Recv()
if err != nil {
e <- err
return
}
select {
case d <- data.Result:
case <-tm:
//exit out go routine
return
}
}
}()
for {
select {
case message := <-d:
msg <- message
case err := <-e:
return err
case <-tm:
return nil
}
}
}
My code above is wrong as: in order for the select to run in the go routines for loop, the blocking function will have to return and data will be populated and therefore won't hit the timeout select case (or will do randomly as both will be ready). Is exiting the parent function enough to exit the go routine?
Use context package WithTimeout. Something like this:
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
// prepare
...
// wait group just for test
var wg sync.WaitGroup
wg.Add(1)
go func() {
for {
select {
case d <- data.Result:
// do something
case <-ctx.Done():
fmt.Println("Done")
wg.Done()
return
}
}
}()
wg.Wait()
cancel()
fmt.Println("Hello, playground")
}
You can see a working example here https://play.golang.org/p/agi1fimtEkJ

How can I completely terminate the running go func() when ctx times out?

When I want ctx timeout, what should I do to completely terminate the method that is executing longRunningCalculation()?
package main
import (
"context"
"log"
"time"
)
func longRunningCalculation(timeCost int) chan string {
result := make(chan string)
go func() {
time.Sleep(time.Second * (time.Duration(timeCost)))
log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
result <- "Done"
log.Println(timeCost)
}()
return result
}
func jobWithTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-ctx.Done():
log.Println(ctx.Err())
return
case result := <-longRunningCalculation(3):
log.Println(result)
}
}
func main() {
jobWithTimeout()
time.Sleep(time.Second * 5)
}
What did you expect to see?
2019/09/25 11:00:16 context deadline exceeded
What did you see instead?
2019/09/25 11:00:16 context deadline exceeded
2019/09/25 11:00:17 Still doing other things...
To stop the goroutine started by longRunningCalculation when the caller's context times out, you need to pass ctx into longRunningCalculation and explicitly handle the context timing out, the same way you do in jobWithTimeout
Doing things that way also means instead of calling time.Sleep, that time.Tick will be a better choice, so both timers are running at the same time. Like so:
package main
import (
"context"
"log"
"time"
)
func longRunningCalculation(ctx context.Context, timeCost int) chan string {
result := make(chan string)
go func() {
calcDone := time.Tick(time.Second * time.Duration(timeCost))
log.Printf("entering select (longRunningCalculation)")
select {
case <-ctx.Done():
result <- "Caller timed out"
return
case <-calcDone:
log.Println("Still doing other things...") //Even if it times out, this goroutine is still doing other tasks.
result <- "Done"
}
log.Println(timeCost)
}()
return result
}
func jobWithTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
result := longRunningCalculation(ctx, 3)
log.Printf("entering select (jobWithTimeout)")
select {
case <-ctx.Done():
log.Println(ctx.Err())
return
case res := <-result:
log.Println(res)
}
}
func main() {
jobWithTimeout()
}

Golang test for go-routine with channel

I’ve the following function which prints dots to the terminal while executing
Some process, the code is working as expected but now I want to test it.
How should I do that
func printdot(finish <-chan struct{}) {
t := time.NewTicker(time.Second)
defer t.Stop()
for {
select {
case <-t.C:
fmt.Print(".")
case <-finish:
return
}
}
}
This is the test
func Test_printdot(t *testing.T) {
finish := make(chan struct{})
start := time.Now()
go printdot(finish)
time.Sleep(1 * time.Second)
sec := time.Since(start).Seconds()
switch int(sec) {
case 0:
// Output:
case 1:
// Output: .
case 2:
// Output: ..
case 3:
// Output: ...
default:
t.Error(“Too much time…”)
}
close(finish)
}
Now the test is continue running without stop even that Im using the finish code , any idea how to improve it ?
Closing a channel doesn't send data, so code will never reach the return in the goroutine. This trick working with range operator. You can do something like this
package main
import (
"fmt"
"time"
"sync"
)
func printdot(finish <-chan struct{}, wg sync.WaitGroup) {
t := time.NewTicker(time.Second)
defer t.Stop()
defer wg.Done()
for {
select {
case <-t.C:
fmt.Print(".")
case <-finish:
return
}
}
}
Notice that I added sync.WaitGroup for "waiting" while goroutine will end
package main
import (
"fmt"
"time"
"sync"
"testing"
)
func Test_printdot(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
finish := make(chan struct{})
start := time.Now()
go printdot(finish, wg)
time.Sleep(3 * time.Second)
sec := time.Since(start).Seconds()
switch int(sec) {
case 0:
// Output:
case 1:
// Output: .
case 2:
// Output: ..
case 3:
// Output: ...
default:
t.Error("Too much time…")
}
finish <- struct{}{}
wg.Wait()
}

Change the time of goroutine sleeping

In Go I can write such code for creating a gorouting that sleeps 5 sec.
func sleep(link chan interface{}){
time.Sleep(5 * time.Second)
fmt.Println("I've slept for 5 sec")
link <- struct {}{}
}
func main() {
var link = make(chan interface{})
go sleep(link)
time.Sleep(1 * time.Second)
// here I want to change the remaining sleeping time of `sleep` goroutine to 0.5 sec
<- link
}
What if in main function I change my mind and decide that the sleeper should sleep not 5 sec but 3. How can it done if goroutine already started to sleep (and sleeping, for example, for 1 sec)?
UPDATE
I mean is there something whereby I can manage that unique goroutine while it sleeps. Like giving commands to it about decreasing or increasing time of sleep:
func main() {
// ...
time.Sleep(1)
something.ManageRemainingTime(10)
time.Sleep(5)
something.ManageRemainingTime(100)
time.Sleep(8)
something.ManageRemainingTime(0.5)
// ...
}
If you just need a way to "wakeup" a sleeping goroutine, you could use sync.Once to ensure your function only gets called once, and then return a channel so you can set a sooner "trigger time", so something this:
func sleep(callback func(), seconds int) chan int {
once := sync.Once{}
wakeup := make(chan int)
go func() {
for sleep := range wakeup {
go func() {
time.Sleep(time.Duration(sleep) * time.Second)
once.Do(callback)
}()
}
}()
wakeup <- seconds
return wakeup
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
t := time.Now()
wakeup := sleep(func() {
fmt.Println("Hello, playground")
wg.Done()
}, 5)
wakeup <- 2
wg.Wait()
fmt.Println(time.Since(t))
}
https://play.golang.org/p/BRNtaBPKpLW
One method is to use a context object. Specifically one created with WithTimeout.
The context object provides a way of sending a "cancel" signal to a worker. In this case your worker is not really doing anything, but still fits the paradigm.
Example would be:
package main
import (
"context"
"fmt"
"sync"
"time"
)
func sleep(ctx context.Context, wg *sync.WaitGroup) {
sctx, _ := context.WithTimeout(ctx, 5*time.Second)
tStart := time.Now()
<-sctx.Done() // will sit here until the timeout or cancelled
tStop := time.Now()
fmt.Printf("Slept for %s\n", tStop.Sub(tStart))
wg.Done()
}
func main() {
ctx, cancelFunc := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(1)
go sleep(ctx, &wg)
if true {
time.Sleep(3 * time.Second)
cancelFunc()
}
wg.Wait()
}
https://play.golang.org/p/2Krx4PsxFKL
Change the if true { to if false { and you can see the Slept for ... change.
One method is to use a timer. You can call Stop() on a timer, but this stops it without waking up whatever is waiting on it. So you then use Reset() to set it to a new value, specifically 0, which triggers it immediately.
Example:
package main
import (
"fmt"
"sync"
"time"
)
func sleep(wg *sync.WaitGroup) *time.Timer {
timer := time.NewTimer(5 * time.Second)
go func() {
tStart := time.Now()
<-timer.C
tStop := time.Now()
fmt.Printf("Slept for %s\n", tStop.Sub(tStart))
wg.Done()
}()
return timer
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
timer := sleep(&wg)
if true {
time.Sleep(3 * time.Second)
if timer.Stop() {
timer.Reset(0)
}
}
wg.Wait()
}
https://play.golang.org/p/b3I65kAujR4
Change the if true { to if false { and you can see the Slept for ... change.

Is this func possible to cause goroutine leak

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?
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)
}
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:
}
}
}
}()

Resources