无法从恐慌中恢复过来 - defer 被完全跳过
Can't recover from panic - defer is entirely skipped
当对无效的编写器使用 io.Copy 时,我会感到恐慌 - 这是预料之中的。但是,在这种情况下我无法恢复。我的延迟恢复被完全绕过了。这是代码:
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/debug"
"cloud.google.com/go/storage"
)
func main() {
var (
ctx = context.Background()
fromFile = "blah.txt"
bucket = "blah-bucket"
path = "blah-path"
)
defer func() {
if result := recover(); result != nil {
buf := make([]byte, 1<<16)
length := runtime.Stack(buf, false)
log.Fatalf("PANIC RECOVER: %v\nSTACK: \n%s", result, buf[:length])
debug.PrintStack()
}
}()
err := FakeUpload(ctx, fromFile, bucket, path)
if err != nil {
fmt.Println(err)
}
fmt.Println("HELLO")
}
func FakeUpload(ctx context.Context, fromFile, toBucket, toPath string) (err error) {
var (
file *os.File
client *storage.Client
wc *storage.Writer
)
defer func() {
for _, c := range []io.Closer{wc, file} {
if c != nil {
err = c.Close()
if err != nil {
return
}
}
}
}()
file, err = os.Open(fromFile)
if err != nil {
err = fmt.Errorf("problem opening file %v: %v", fromFile, err)
return
}
wc = client.Bucket(toBucket).Object(toPath).NewWriter(ctx)
_, err = io.Copy(wc, file) // THE UNRECOVERABLE PANIC HAPPENS HERE
return
}
恐慌是:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x9e0fa8]
goroutine 21 [running]:
cloud.google.com/go/storage.(*Writer).open.func1(0xc000161200, 0xc0002721e0, 0xc000150388, 0xc000290e20, 0x1, 0x1)
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:128 +0x248
created by cloud.google.com/go/storage.(*Writer).open
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:118 +0x6ce
Process exiting with code: 0
module main
go 1.15
require cloud.google.com/go/storage v1.14.0
go version go1.15.10 windows/amd64
关键是,如果它在其他地方发生恐慌,例如我指向一个无效文件,它会恐慌,并且延迟恢复会正确捕获它。
这让我很困惑。有什么想法吗?
正如您在上面的评论中看到的那样,答案是 cloud.google.com/go/storage
作者正在创建一个 goroutine,并将 panic 扔在那里。 GO 不允许你从另一个 goroutine 中恢复。
当对无效的编写器使用 io.Copy 时,我会感到恐慌 - 这是预料之中的。但是,在这种情况下我无法恢复。我的延迟恢复被完全绕过了。这是代码:
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"runtime"
"runtime/debug"
"cloud.google.com/go/storage"
)
func main() {
var (
ctx = context.Background()
fromFile = "blah.txt"
bucket = "blah-bucket"
path = "blah-path"
)
defer func() {
if result := recover(); result != nil {
buf := make([]byte, 1<<16)
length := runtime.Stack(buf, false)
log.Fatalf("PANIC RECOVER: %v\nSTACK: \n%s", result, buf[:length])
debug.PrintStack()
}
}()
err := FakeUpload(ctx, fromFile, bucket, path)
if err != nil {
fmt.Println(err)
}
fmt.Println("HELLO")
}
func FakeUpload(ctx context.Context, fromFile, toBucket, toPath string) (err error) {
var (
file *os.File
client *storage.Client
wc *storage.Writer
)
defer func() {
for _, c := range []io.Closer{wc, file} {
if c != nil {
err = c.Close()
if err != nil {
return
}
}
}
}()
file, err = os.Open(fromFile)
if err != nil {
err = fmt.Errorf("problem opening file %v: %v", fromFile, err)
return
}
wc = client.Bucket(toBucket).Object(toPath).NewWriter(ctx)
_, err = io.Copy(wc, file) // THE UNRECOVERABLE PANIC HAPPENS HERE
return
}
恐慌是:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x9e0fa8]
goroutine 21 [running]:
cloud.google.com/go/storage.(*Writer).open.func1(0xc000161200, 0xc0002721e0, 0xc000150388, 0xc000290e20, 0x1, 0x1)
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:128 +0x248
created by cloud.google.com/go/storage.(*Writer).open
C:/Users/xxxx/go/pkg/mod/cloud.google.com/go/storage@v1.14.0/writer.go:118 +0x6ce
Process exiting with code: 0
module main
go 1.15
require cloud.google.com/go/storage v1.14.0
go version go1.15.10 windows/amd64
关键是,如果它在其他地方发生恐慌,例如我指向一个无效文件,它会恐慌,并且延迟恢复会正确捕获它。
这让我很困惑。有什么想法吗?
正如您在上面的评论中看到的那样,答案是 cloud.google.com/go/storage
作者正在创建一个 goroutine,并将 panic 扔在那里。 GO 不允许你从另一个 goroutine 中恢复。