I used to think the panic in a goroutine will kill the program if its caller finishes before the panic (the deferred recovering gives no help since at that point there's no panic occurs yet),
until I tried following code:
func fun1() {
fmt.Println("fun1 started")
defer func() {
if err := recover(); err != nil {
fmt.Println("recover in func1")
}
}()
go fun2()
time.Sleep(10 * time.Second) // wait for the boom!
fmt.Println("fun1 ended")
}
func fun2() {
fmt.Println("fun2 started")
time.Sleep(5 * time.Second)
panic("fun2 booom!")
fmt.Println("fun2 ended")
}
I found no matter the caller function finishes or not, if the goroutines it starts panic, the caller's deferred recover mechanism will not help. The whole program is still dead.
So, WHY? Theoretically the caller function is still running. When the panics happen the caller's deferred functions should work (including the recovering).
The mechanism is called panic/recover. We can call the built-in panic function to create a panic to make the current goroutine enter panicking status. Panicking is another way to make a function return. Once a panic occurs in a function call, the function call returns immediately and enters its exiting phase.
By comparing the err variable against nil , we can detect if a panic occurred, and in this case we log the panic using the log. Println function, as though it were any other error . Following this deferred anonymous function, we call another function that we defined, divide , and attempt to print its results using fmt.
Recover and catch a panicThe built-in recover function can be used to regain control of a panicking goroutine and resume normal execution. A call to recover stops the unwinding and returns the argument passed to panic . If the goroutine is not panicking, recover returns nil .
The recover() function in Go Language is used to recover from a goroutine that is in panic, meaning it helps recover from an error that has been raised. The program takes control through recover rather than letting the code crash. recover() is an inbuilt function in Go Language.
The specification says:
While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F's caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking.
Because fun2
is the top-level function executing in the goroutine and fun2
does not recover from a panic, the program terminates when fun2
panics.
The deferred call in fun1
is not called when the goroutine executing fun2
panics.
A goroutine cannot recover from a panic in another goroutine.
Instead of recovering in fun1() you can use runtime.Goexit() in fun2() which will
Goexit terminates the goroutine that calls it. No other goroutine is affected.
Something like
func fun2() {
defer func() {
if err := recover(); err != nil {
fmt.Println("Do some cleanup and teardown")
runtime.Goexit() //Here
}
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With