Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No way to catch errors from goroutines automatically #20161

Closed
muravjov opened this issue Apr 28, 2017 · 14 comments
Closed

No way to catch errors from goroutines automatically #20161

muravjov opened this issue Apr 28, 2017 · 14 comments

Comments

@muravjov
Copy link

muravjov commented Apr 28, 2017

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

$ go version
go version go1.7.5 linux/amd64

What operating system and processor architecture are you using (go env)?

$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/opt/programming/golang/git"
GOTOOLDIR="/opt/programming/golang/git/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build930744821=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"

What did you do?

Thanks to go keyword it is easy to write concurrent algorithms, but also it is easy to make errors, which one cannot catch. For example:

package main

func main() {
	go func(){
		// = panic() = exit(2) + print(stacktrace)
		i := 0
		i = 1 / i
	}()
}

And you cannot catch it unless you manually add defer to every go invocation, like that:

func main() {
	go func(){
		defer func() {
			err := recover()
			...
			panic(err)
		}()

		// = panic() = exit(2) + print(stacktrace)
		i := 0
		i = 1 / i
	}()
}

You may catch such errors only by reading stderr from target process and guessing it is a Go stacktrace. Which is a hack.

What did you expect to see?

I need a mechanism to catch errors and send them with tracebacks (e.g. into https://sentry.io ). Like postmortem library Breakpad for C++ .

But there is no way to hook a defer to every new goroutine. It may be realized via special API to call newdefer/deferproc internal function from runtime/panic.go .

P.S. Similar discussion is about atexit , where Ian Lance Taylor writes that
"The only fully reliable mechanism is a wrapper program ..."

@ianlancetaylor
Copy link
Contributor

This has been discussed before. It is not going to change at this point.

You can fix it in your own code, for your own goroutines, by using a function like

func Go(f func()) {
defer func() {
if r := recover(); r != nil {
// do something
}
}()
go f()
}

For the general case, use a wrapper program.

If you want to raise a proposal with a specific suggestion for how to address this general problem, please feel free to use the proposal process described at https://github.com/golang/proposal/blob/master/README.md . I don't personally think that such a proposal is likely to be accepted, but we are always open to good new ideas.

This issue has no specific suggestion for how to fix this, so I am going to close this.

@iigorr
Copy link

iigorr commented Nov 30, 2017

@ianlancetaylor: Your first suggestion does not work. The Go function would not recover a panic in f(). This is the actual problem here. See https://play.golang.org/p/YP5n7-lzcz

@cznic
Copy link
Contributor

cznic commented Nov 30, 2017

@President
Copy link

This "func Go" wrapper is not a solution - we may have many functions with very different parameters - so we need to create many Go-wrappers? It is not usable at all.
We need easy way to wrap catch all panics from all goroutines!

@as
Copy link
Contributor

as commented Aug 17, 2018

The generalized problem statement, I believe, is that any goroutine can call go panic("") and explode the program.

@cznic https://play.golang.org/p/N6NGMFH86g2

In my experience, this is only a problem when consumers of a buggy API want to avoid panic due to implementation flakiness from third party code. The solution in that case is to properly vet third party code, not implement a poor-man's try catch generic exception handler around the whole process.

@ofux
Copy link

ofux commented May 16, 2019

Why was this issue closed without any further discussion?

@randall77
Copy link
Contributor

Because this issue really needs a proposal for it to be actionable.

@calixwu
Copy link

calixwu commented Dec 4, 2019

So is there a proposal now?

@President
Copy link

FYI: We finally end up with wrapper and prohibiting the team to use "go" operator directly.
However I still dislike this solution. Language should provide a way to catch all errors w/o wrapping every "go" operator.

recovery.SafeGo(logger, func() {
              method(all parameters)
	})

func SafeGo(logger logging.ILogger, f func()) {
	go func() {
		defer func() {
			if panicMessage := recover(); panicMessage != nil {
				stack := debug.Stack()

				logger.Errorf("RECOVERED FROM UNHANDLED PANIC: %v\nSTACK: %s", panicMessage, stack)
			}
		}()

		f()
	}()
}

@ianlancetaylor
Copy link
Contributor

@calixwu There is no proposal here. If somebody has a proposal, please open a new issue. See https://golang.org/s/proposal. But it is very unlikely that the language will change. People who want to use SafeGo as in the previous comment can already write it.

@calixwu
Copy link

calixwu commented Dec 5, 2019

@ianlancetaylor ok...

@avivmag
Copy link

avivmag commented Jan 28, 2020

FYI: We finally end up with wrapper and prohibiting the team to use "go" operator directly.
However I still dislike this solution. Language should provide a way to catch all errors w/o wrapping every "go" operator.

recovery.SafeGo(logger, func() {
              method(all parameters)
	})

func SafeGo(logger logging.ILogger, f func()) {
	go func() {
		defer func() {
			if panicMessage := recover(); panicMessage != nil {
				stack := debug.Stack()

				logger.Errorf("RECOVERED FROM UNHANDLED PANIC: %v\nSTACK: %s", panicMessage, stack)
			}
		}()

		f()
	}()
}

I just want to add a note that although it really is a nice workaround, whoever use that must keep in mind about the common mistake of 'Using goroutines on loop iterator variables'

@mangelajo
Copy link

It's sad there is no way to have a global panic capture embedded in the language,.... this issue may be re-opened and considered.

@ianlancetaylor
Copy link
Contributor

@mangelajo I'm sorry, but this issue is closed. Please see the comment above: #20161 (comment).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests