Reputation: 1213
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
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