Stopping a bufio.Scanner with a stop channel

Viewed 938

I'm writing something that reads lines from os.Stdin using a bufio.Scanner like:

for s.scanner.Scan() {
  line := s.scanner.Text()
  // process line
}

This is running in a goroutine and I want to be able to stop it when a chan struct{} closes. However as Scan blocks until there is another line, I'm at a loss as how to stop it, if there is no more input, it'll block indefinitely.

Can anyone point me in the right direction here?

1 Answers

By creating one more indirection and ignoring the underlying, we can stop.

// actual reading, converts input stream to a channel
func readUnderlying(lines chan interface{}) {
    s := bufio.NewScanner(os.Stdin)
    for s.Scan() {
        lines <- s.Text()
    }
    lines <- s.Err()
}

func read(stop chan struct{}) {
    input := make(chan interface{}) // input stream
    go readUnderlying(input) // go and read
    for {
        select { // read or close
        case lineOrErr := <-input:
            fmt.Println(lineOrErr)
        case <-stop:
            return
        }
    }
}

func main() {
    stop := make(chan struct{})
    go read(stop)

    // wait some to simulate blocking
    time.Sleep(time.Second * 20) // it will print what is given
    close(stop)
    time.Sleep(time.Second * 20) // stopped so no more processing
}
Related