3

According to Go's specifications:

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.

This means that if a goroutine panics and is not recovered, the application terminates.
Aside from the fact that an unhandled panic can be an easy bug to miss, this also puts my application at the mercy of imported packages which may start goroutines with unhandled panics. If an imported package panics in this way, my application will terminate and, as far as I am aware, there is no way to recover.
In a production environment, keeping my application alive is mission-critical, and this behavior poses a problem. My question is twofold:

  1. What is the "philosophical" idea behind this design choice? Why did Go developers decide to omit isolation between lifecycles of goroutines?
  2. Is there some way that I missed to keep goroutines alive in the case where a different goroutine panics?
  • 2
    «Why did Go developers decide to omit isolation between lifecycles of goroutines?» mostly because while Go is a "safe" language—it's only safe in the sense, say, accessing a slice element off the slice's bounds will produce a runtime panic—rather than an undefined behaviour as in C or the like… – kostix Apr 14 '22 at 09:27
  • 2
    …So basically when the Go runtime detects a situation which _had not be there in the first place_ —such as accesing a `nil` pointer, out-of-bounds access, integer division by zero etc, it cannot guarantee the problem which led to that situation _was somehow contained in the goroutine which were executing that code._ Consider running `go someFunction(somePointer)` which itself calls `go someOtherFunction(thatPointer)` which eventually dereferences the pointer and crashes, with `somePointer` having been set somewhere long before the first goroutine was spawned. – kostix Apr 14 '22 at 09:28
  • 1
    Probably the isolation will add a significant overhead and goroutines are pretty light. Also you should not use panic as error handling. And there are exceptions that you can’t even use defer + recover to handle (like the ones from cgo / native code). Do You want isolation? Write the necessary code if needed. But there are consequences (like performance) – Tiago Peczenyj Apr 14 '22 at 09:32
  • 2
    Another problem is that goroutines in Go do not somehow automagically "phone home" about the outcome of their execution when they complete it, so if the runtime were to crash just a single goroutine, there would be a question of how to actually handle that. Say, what if another goroutine waits for the goroutine which crashed to update some variable protected by a lock? The waiting goroutine would wait indefinitely. – kostix Apr 14 '22 at 09:33
  • 2
    In other words, only the developer knows whether it's OK to consider some specific gorotuines as "self-contained enough" to sensibly catch runtime panics in them. One good example is `net/http`: it traps panics in `net/http.Server` request handlers. While the devs expressed they regret that design decition, it's not _that_ bad, actually. – kostix Apr 14 '22 at 09:35
  • 1
    Well, and while we're at it, your question is off-topic. You should have read [this](https://stackoverflow.com/help/on-topic) before posting, but please do this anyway. Questions which would provoke opinionated answers should be asked using other venues; please refer [there](https://github.com/golang/go/wiki#the-go-community). – kostix Apr 14 '22 at 09:38
  • If staying alive is mission critical, you may protect 3rd party calls to recover from them. See [Generic panic recovering in go programs](https://stackoverflow.com/questions/56234983/generic-panic-recovering-in-go-programs/56235139#56235139). But you still have to decide what to do when they panic. – icza Apr 14 '22 at 10:04
  • "...this also puts my application at the mercy of imported packages which may start goroutines with unhandled panics" this fact scares me too. While it's possible to add `wrap()` calls to our own code the imported package is till a blocker. So there is literally NO WAY to avoid crashes. Maybe Go developers expected that mission critical apps must be monitored and recovered on the OS level. Like Kubernetes restarts dead pods. – neleus Mar 17 '23 at 18:21

0 Answers0