Hi,
I have two long running things to do, I'd like to do them in parallel, then sync up at the end. I've written it as a function which gets one thing done, and which backgrounds the second thing with a goroutine. Thing one has a lot of reasons to exit early, and I need to signal those to thing two so that the go routine can exit cleanly and not create a goroutine leak. Standard fare, usually solved by introducing a channel and a select statement. I want to signal done for early exits, including panics, so I thought I would write to the done channel in a defer block. That caused a problem, because in the happy case, both the foreground and the background routines are finished, the done channel has no reader, so writing to it in the defer block causes "fatal error: all goroutines are asleep - deadlock!" So I introduced a new boolean variable ("clean_finish") that is false until the background job completes, and which is tested in the defer block before writing to the done channel. Is there anything wrong with my approach? You can see the code in the playground link above, or down below.
package main
import (
"sync/atomic"
"fmt"
"time"
)
func main() {
ch := make(chan int)
done := make(chan struct{})
var clean_finish uint32
defer func() {
fin := atomic.LoadUint32(&clean_finish)
if fin != 1 {
done <- struct{}{}
}
}()
go func() {
defer func() {
fmt.Println("we're done here")
}()
select {
case <-time.After(time.Second * 1):
ch <- 1
atomic.StoreUint32(&clean_finish, 1)
case <- done:
fmt.Println("early exit")
}
}()
panic("bye") // comment this out to see normal behavior
n := <- ch
fmt.Printf("n: %v\n", n)
}