felipeek
felipeek

Reputation: 1213

Stop goroutine that is blocked gracefully

I have a goroutine that is constantly blocked reading the stdin, like this:

func routine() {
    for {
        data := make([]byte, 8)
        os.Stdin.Read(data);
        otherChannel <-data
    }
}

The routine waits to read 8 bytes via stdin and feeds another channel.

I want to gracefully stop this goroutine from the main thread. However, since the goroutine will almost always be blocked reading from stdin, I can't find a good solution to force it to stop. I thought about something like:

func routine(stopChannel chan struct{}) {
    for {
        select {
        case <-stopChannel:
            return
        default:
            data := make([]byte, 8)
            os.Stdin.Read(data);
            otherChannel <-data
        }
    }
}

However, the problem is that if there is no more input in the stdin when the stopChannel is closed, the goroutine will stay blocked and not return.

Is there a good approach to make it return immediately when the main thread wants?

Thanks.

Upvotes: 5

Views: 697

Answers (1)

LeGEC
LeGEC

Reputation: 52141

To detect that os.Stdin has been closed : check the error value returned by os.Stdin.Read().

One extra point : although you state that in your case you will always receive 8 bytes chunks, you should still check that you indeed received 8 bytes of data.

func routine() {
    for {
        data := make([]byte, 8)
        n, err := os.Stdin.Read(data)

        // error handling : the basic thing to do is "on error, return"
        if err != nil {
            // if os.Stdin got closed, .Read() will return 'io.EOF'
            if err == io.EOF {
                log.Printf("stdin closed, exiting")
            } else {
                log.Printf("stdin: %s", err)
            }
            return
        }

        // check that 'n' is big enough :
        if n != 8 {
            log.Printf("short read: only %d byte. exiting", n)
            return  // instead of returning, you may want to keep '.Read()'ing
                    // or you may use 'io.ReadFull(os.Stdin, data)' instead of '.Read()'
        }

        // a habit to have : truncate your read buffers to 'n' after a .Read()
        otherChannel <-data[:n]
    }
}

Upvotes: 1

Related Questions