Repository navigation
testing: Example test with runtime.Goexit hangs #41084
Copy link
Copy link
Closed
Labels
FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.help wanted
Milestone
Description
What version of Go are you using (go version)?
$ go version go version go1.15 linux/amd64
(Also verified on 1.14 on both linux and darwin)
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="on" GOARCH="amd64" GOBIN="" GOCACHE="/home/user/.cache/go-build" GOENV="/home/user/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/opt/go/path/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/opt/go/path" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/home/user/.gimme/versions/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/home/user/.gimme/versions/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/home/user/go-test/go.mod" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build701379955=/tmp/go-build -gno-record-gcc-switches"
What did you do?
Used runtime.Goexit in an example test from the main test goroutine.
package main
import "runtime"
func ExampleGoexit() {
runtime.Goexit()
// Output:
}What did you expect to see?
A failure, similar to when runtime.Goexit is used in a test,
package main
import "runtime"
import "testing"
func TestGoexit(t *testing.T) {
runtime.Goexit()
}> go test -v -run Test
=== RUN TestGoexit
--- FAIL: TestGoexit (0.00s)
panic: test executed panic(nil) or runtime.Goexit
[...]
What did you see instead?
The example test hangs (until the test timeout is hit):
> go test -v -run Example -timeout 10s
=== RUN ExampleGoexit
--- PASS: ExampleGoexit (0.00s)
panic: test timed out after 10s
goroutine 8 [running]:
testing.(*M).startAlarm.func1()
[...]/go/src/testing/testing.go:1609 +0xe5
created by time.goFunc
[...]/go/src/time/sleep.go:167 +0x45
exit status 2
FAIL goexit_test 10.008s
I can send a SIGQUIT to get the backtrace:
=== RUN ExampleGoexit
--- PASS: ExampleGoexit (0.00s)
^\SIGQUIT: quit
PC=0x7fff6a9c4766 m=0 sigcode=0
goroutine 0 [idle]:
runtime.kevent(0x7ffe00000003, 0x0, 0x0, 0x7ffeefbfece8, 0x40, 0x7ffeefbfecc0, 0x0)
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/sys_darwin.go:355 +0x39
runtime.netpoll(0x25408e43c, 0xadf60b77b01)
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/netpoll_kqueue.go:120 +0xae
runtime.findrunnable(0xc000026800, 0x0)
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/proc.go:2323 +0x72b
runtime.schedule()
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/proc.go:2520 +0x2fc
runtime.park_m(0xc000000180)
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/proc.go:2690 +0x9d
runtime.mcall(0x105f4a6)
/usr/local/Cellar/go/1.14.2_1/libexec/src/runtime/asm_amd64.s:318 +0x5b
In the Go playground, this triggers a deadlock:
https://play.golang.org/p/xaSFhxBPeKB
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.help wanted
Activity
zigo101 commentedon Aug 28, 2020
Is it the documented behavior?
[-]testing: Example test with runtime.Goexit hangs[/-][+]runtime: Example test with runtime.Goexit hangs[/+]odeke-em commentedon Aug 29, 2020
Thank you for filing this @prashantv, and thank you @go101 for responding to @prashantv’s question. Indeed as @go101 points out, that is the behavior of Goexit(), and in the main test code, there are other goroutines running in the background and as documented it can’t return until all the other goroutines have. I shall close this issue, but thank you again.
prashantv commentedon Aug 29, 2020
Hi @odeke-em, I think this is a testing package issue, since
TestGoexitworks correctly (as I documented in my issue), whileExampleGoexithangs. Shouldn't they both behave in the same way?The
Goexitfunction is expected to end the goroutine, but the testing package handles this correctly forTestXfunctions, but not forExampleXfunctions.odeke-em commentedon Aug 29, 2020
@prashantv there are other goroutines running for Example as I had mentioned and for example if you see the implementation in
go/src/testing/run_example.go
Line 21 in 4fc3896
by you invoking runtime.Goexit() you are killing that goroutine in its main without ever returning yet there are other goroutines that haven't yet been terminated and are waiting for output. This is an implementation detail about Example tests that I don't think needs to be changed.
You can validate that the goroutines are running by printing the stack of the entire system like this
which will produce
and you'll see the goroutine that's still waiting in there.
prashantv commentedon Aug 29, 2020
I understand why it's hanging, but it doesn't hang for
TestXfunctions, since there's explicit logic to detect and handle this case,go/src/testing/testing.go
Line 1069 in 4fc3896
This logic doesn't exist for example tests, and so it hangs, which doesn't seem ideal. It seems beneficial to users to detect a
runtime.Goexitorpanic(nil)in an example test the same way as aTestXfunction, and in general for consistency.Perhaps I'm missing a reason for why Example tests should not have consistent behaviour with standard test functions, I thought it might have just been overlooked.
odeke-em commentedon Aug 29, 2020
Thank you for the follow-up question @prashantv, you raise a great concern which I don't have an answer to off the top of my head, thus I shall re-open this for investigation and other comments.
[-]runtime: Example test with runtime.Goexit hangs[/-][+]testing: Example test with runtime.Goexit hangs[/+]cagedmantis commentedon Aug 30, 2020
@prashantv I misunderstood the issue and incorrectly renamed it with the runtime package. It's been corrected.
cagedmantis commentedon Aug 30, 2020
/cc @mpvl
bcmills commentedon Aug 31, 2020
In theory we could run each
Examplefunction in its own goroutine, and move the output-checking to adeferstatement so that it can trigger even in case of aGoexit.That said, use of
runtime.Goexitoutside of thetestingpackage itself should be rare enough that I'm not sure this is worth the risk of the refactoring. (If someone wants to contribute a fix, we can see just how invasive it is and decide at that point.)changkun commentedon Aug 31, 2020
@bcmills I don't know refactoring to run
Examplein its own goroutine is the planned approach. But the current examples are executed sequentially, it seems that I can send a simple CL to fix what was reported originally. Should I do that?bcmills commentedon Aug 31, 2020
If the examples are executed sequentially on the same goroutine, then a
runtime.Goexitin anExampleis always wrong. A change to panic in that case instead of deadlocking seems like it would be fine, but it still wouldn't let you safely write thatExampleGoexitfunction.changkun commentedon Aug 31, 2020
@bcmills Thanks for the quick response. What's the behavior to safely write
ExampleGoexit? I thought the bug report is expecting to fail theExampleGoexitand panicking onerrNilPanicOrGoexitwhereas the current behavior is getting a PASS onExampleGoexitwith a long pause.gopherbot commentedon Aug 31, 2020
Change https://golang.org/cl/251857 mentions this issue:
testing: fail Example tests that involves runtime.Goexittesting: fail Example tests that invoke runtime.Goexit