1

I've been trying to find something that answers this question but I can't find anything that talks about it.

Lets say I have a function in Go which is something like this:

func main() {
    // assume this wrapped in a waitgroup or something 
    // so that it doesnt exit
    go queue.ConsumeAndDoSomething()
    go api.StartServer()
}

I have two goroutines here that do completely different things and one should ideally keep running if the other crashes/panics. If the queue operation fails, the API server should be impacted and vice versa.

I'm not sure if this possible (or even recommended). Is there a clean way of doing this or should the whole program exit once a goroutine panics?

ninesalt
  • 4,054
  • 5
  • 35
  • 75
  • I just tried a `panic` in one of the goroutines and the whole program exited – ninesalt Mar 04 '21 at 13:03
  • 1
    Use `recover` to recover from a panic. – Jonathan Hall Mar 04 '21 at 13:30
  • Though it's worth noting that the purpose of `panic` is to crash the application; cases where `recover` is appropriate are actually fairly rare (e.g. `net/http`'s server will recover any time a handler panics, so that one handler won't crash the whole server) and often indicate that the `panic` shouldn't be there in the first place, it should have just been an error. – Adrian Mar 04 '21 at 14:25

2 Answers2

4

You have to use the builtin recover() function to recover from panics, and you have to call it in a deferred function.

Let's say you have a function that may panic:

func doPanic() {
    log.Println("about to panic")
    panic("test")
}

Create a helper function to launch a function as a goroutine "protected" (from panics):

func protect(f func()) {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Recovered: %v", err)
        }
    }()

    f()
}

And use it like this:

func main() {
    go protect(doPanic)

    for {
        time.Sleep(time.Second)
        fmt.Println("tick")
    }
}

This test app will output:

2021/03/04 14:12:31 about to panic
2021/03/04 14:12:31 Recovered: test
tick
tick
tick
...

See related question: Generic panic recovering in go programs

icza
  • 389,944
  • 63
  • 907
  • 827
  • I'm thinking it might be easier to exit the whole program since the goroutine wont restart. Any idea what is the recommended practice or convention here? – ninesalt Mar 04 '21 at 13:17
  • @ninesalt Ideally / normally a goroutine shouldn't panic. The goroutine itself should handle the error-and-retry-operation. If something goes wrong inside it from which it can recover, it should retry. – icza Mar 04 '21 at 13:22
1

Each goroutine must defer a recover call to recover from a potential panic if you want other goroutine to not be impacted.

Instead of

go queue.ConsumeAndDoSomething()

You should use

go func(){
    defer func() {
            if r := recover(); r != nil {
                log.Error("goroutine paniqued: ", r)
            }
        }()
    queue.ConsumeAndDoSomething()
}() 
aureliar
  • 1,476
  • 4
  • 16