通过恢复到上goroutine golang

I created an example where I run function concurrently inside which I panic and recover:

package main

import "fmt"

func main() {
    // "main" recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("main goroutine paniced:", r)
        }
    }()

    // running function concurrently inside which I panic
    chanStr := make(chan string)
    go func() {
        // this "internal" goroutin recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("internal goroutine paniced:", r)
            }
            chanStr <- "hello world"
        }()
        // panicking and wanting recovery not only in "internal" recovery but in "main" recovery as well
        panic("NOT main goroutine")
    }()
    // waiting for chan with "internal" goroutine panicking and recovery
    str := <-chanStr
    fmt.Println(str)

    // panic("main")
}

It gives output:

internal goroutine panicked: NOT main goroutine
hello world

Is it possible to change my code to make pass recovery from "internal" to "main"? In other words I want it to write down to console:

internal goroutine paniced: NOT main goroutine
main goroutine paniced: main
hello world

I tried to implement this by removing "internal" recovery func at all, but "main" recovery do not recover panic inside "internal" goroutine in this case.

Playground

Update

I tried to follow @Momer's advice and send an error through the channel and handle it in the main goroutine, instead of trying to bubble the panic up:

package main

import (
    "errors"
    "fmt"
)

func main() {
    // "main" recovery
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("main goroutine paniced:", r)
        }
    }()

    // running func concarantly inside which I panic
    chanStr := make(chan string)
    chanErr := make(chan error)
    var err error
    go func() {
        // this "internal" goroutin recovery
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("internal goroutine paniced:", r)
                switch t := r.(type) {
                case string:
                    fmt.Println("err is string")
                    err = errors.New(t)
                case error:
                    fmt.Println("err is error")
                    err = t
                default:
                    fmt.Println("err is unknown")
                    err = errors.New("Unknown error")
                }

                chanErr <- err
                chanStr <- ""
            }
        }()
        // panicing and wanting recovery not only in "internal" recovery but in "main" recovery as well
        panic("NOT main goroutine")
        chanStr <- "hello world"
        chanErr <- nil

    }()
    // waiting for chan with "internal" goroutin panicing and recovery
    str := <-chanStr
    err = <-chanErr
    fmt.Println(str)
    fmt.Println(err)

    // panic("main")
}

It gives error

all goroutines are asleep - deadlock

Full output:

go run /goPath/parentRecoverty2.go
internal goroutine paniced: NOT main goroutine
err is string
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /goPath/parentRecoverty2.go:48 +0x1d4

goroutine 5 [chan send]:
main.func·002()
    /goPath/parentRecoverty2.go:37 +0x407
main.func·003()
    /goPath/parentRecoverty2.go:42 +0x130
created by main.main
    /goPath/parentRecoverty2.go:46 +0x190
exit status 2

Update playground

In your updated question, one thread is blocked by reading from chanStr while the other thread is blocked by writing into chanErr. Switching the order of writes should resolve the deadlock.

defer func() {
    if r := recover(); r != nil {
        fmt.Println("internal goroutine paniced:", r)
        switch t := r.(type) {
        case string:
            fmt.Println("err is string")
            err = errors.New(t)
        case error:
            fmt.Println("err is error")
            err = t
        default:
            fmt.Println("err is unknown")
            err = errors.New("Unknown error")
        }
        chanStr <- ""
        chanErr <- err
    }
}

I took panic/recover in golang as try/catch/final blocks in java or c++.

For more detail, you can visit Handling panics (from Golang spec).

so you can pass a panic to method's caller. a simple code is below, hope it helps

Note: in function Foo(), I use recover() to catch things going wrong, and then re-panic in order to catch it later in outer caller.

package main

import (
    "fmt"
    "time"
)

func Foo() {
    defer func() {
        if x := recover(); x != nil {
            fmt.Printf("Runtime panic: %v 
", x)
            panic("Ah oh ... Panic in defer")
        }
    }()
    panic("Panic in Foo() !")
}

func Game() {
    defer func(){
        fmt.Println("Clean up in Game()")
    }()

    defer func() {
        if x := recover(); x != nil {
            fmt.Println("Catch recover panic !!! In Game()")
        }
    }()
    Foo()

}

func main() {

    defer func() {
        fmt.Println("Program Quit ... ")
    }()

    fmt.Println("-----------Split-------------")
    go Game()
    time.Sleep(1 * time.Millisecond)
    fmt.Println("-----------Split-------------")
}