Silk13
Silk13

Reputation: 57

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?

Upvotes: 2

Views: 3844

Answers (1)

Lucas Katayama
Lucas Katayama

Reputation: 4570

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

Upvotes: 3

Related Questions