How to detect panic (zero) and normal execution in a deferred Go function?

Runtime can detect panic(nil) and reports an error.

However, I cannot detect panic(nil) with recover() in the red defer function because it returns nil , so I cannot distinguish it from normal execution (without panic), as I would test the return value of recover() is zero.

For instance,

 defer func(){ var err = recover() if err != nil { // Real serious situation. Panic from inner code. // And we may have some critical resources which // must be cleaned-up at any cases. // However, this will not be executed for panic(nil) rollback() // I am still not sure that how should I treat `panic`… // Should I just ignore them? } }() var err = doTransaction() if err == nil { commit() // Happy case. } else { rollback() // Regular execution. Just a lucky case. } 

ROLLBACK is just an example, and I think I might have many critical cases that need to be cleaned up. Well, these cleaning codes will not be executed even if the program crashes, but I want to protect as much as possible.

How can I detect any panic regardless of its parameter in a delayed function?

+6
source share
2 answers

If I do not understand your question, deferred function calls will be executed during a panic, even if the passed value was nil . This is illustrated by the following program :

 package main import "fmt" func main() { defer func() { fmt.Println("Recover:", recover()) }() panic(nil) } 

This way you can easily detect if panic(nil) happened by comparing the value returned with recover() to nil .

Change response to comment:

Yes it's true; deferred calls are usually made when the function returns. But they also start when unwinding the call stack after panic() .

Edit after question has been updated:

You are right that there is no way to differentiate these cases. On the other hand, panic with nil doesn't make much sense either - especially because of this limitation.

The only use case for panic(nil) that I could think of would be to intentionally prevent recovery and force a program crash using stack trace. There are more elegant ways to do this, though, for example, using the runtime package.

+4
source

I can just set the flag before exiting.

AFAIK, panic is goroutine-specific, and a single goroutine is guaranteed to be in a single thread. Therefore, the ok variable does not require synchronization / locking. If I am wrong, please correct me.

 func clean(ok *bool) { if *ok { log.Printf("Execution OK. No panic detected.\n") } else { var reason = recover() log.Printf("Some bad thing happen. reason = %v\n", reason) panic("Abnormal exit. Program abandoned. Stack-trace here.") debug.PrintStack() // Oops. this will not run. } } func main() { var ok bool = false defer clean(&ok) panic(nil) test1() // Here the main job. ok = true log.Printf("All work done. Quit gracefully.\n") } 
+2
source

Source: https://habr.com/ru/post/956957/


All Articles