mirror of
https://github.com/golang/go
synced 2024-09-05 16:04:50 +00:00
![Austin Clements](/assets/img/avatar_default.png)
Currently, gentraceback tracks the closure context of the outermost frame. This used to be important for "unstarted" calls to reflect function stubs, where "unstarted" calls are either deferred functions or the entry-point of a goroutine that hasn't run. Because reflect function stubs have a dynamic argument map, we have to reach into their closure context to fetch to map, and how to do this differs depending on whether the function has started. This was discovered in issue #25897. However, as part of the register ABI, "go" and "defer" were made much simpler, and any "go" or "defer" of a function that takes arguments or returns results gets wrapped in a closure that provides those arguments (and/or discards the results). Hence, we'll see that closure instead of a direct call to a reflect stub, and can get its static argument map without any trouble. The one case where we may still see an unstarted reflect stub is if the function takes no arguments and has no results, in which case the compiler can optimize away the wrapper closure. But in this case we know the argument map is empty: the compiler can apply this optimization precisely because the target function has no argument frame. As a result, we no longer need to track the closure context during traceback, so this CL drops all of that mechanism. We still have to be careful about the unstarted case because we can't reach into the function's locals frame to pull out its context (because it has no locals frame). We double-check that in this case we're at the function entry. I would prefer to do this with some in-code PCDATA annotations of where to find the dynamic argument map, but that's a lot of mechanism to introduce for just this. It might make sense to consider this along with #53609. Finally, we beef up the test for this so it more reliably forces the runtime down this path. It's fundamentally probabilistic, but this tweak makes it better. Scheduler testing hooks (#54475) would make it possible to write a reliable test for this. For #54466, but it's a nice clean-up all on its own. Change-Id: I16e4f2364ba2ea4b1fec1e27f971b06756e7b09f Reviewed-on: https://go-review.googlesource.com/c/go/+/424254 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Austin Clements <austin@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com>
52 lines
1.1 KiB
Go
52 lines
1.1 KiB
Go
// run
|
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Make sure the runtime can scan args of an unstarted goroutine
|
|
// which starts with a reflect-generated function.
|
|
|
|
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
)
|
|
|
|
const N = 100
|
|
|
|
func main() {
|
|
runtime.GOMAXPROCS(1)
|
|
// Run GC in a loop. This makes it more likely GC will catch
|
|
// an unstarted goroutine then if we were to GC after kicking
|
|
// everything off.
|
|
go func() {
|
|
for {
|
|
runtime.GC()
|
|
}
|
|
}()
|
|
c := make(chan bool, N)
|
|
for i := 0; i < N; i++ {
|
|
// Test both with an argument and without because this
|
|
// affects whether the compiler needs to generate a
|
|
// wrapper closure for the "go" statement.
|
|
f := reflect.MakeFunc(reflect.TypeOf(((func(*int))(nil))),
|
|
func(args []reflect.Value) []reflect.Value {
|
|
c <- true
|
|
return nil
|
|
}).Interface().(func(*int))
|
|
go f(nil)
|
|
|
|
g := reflect.MakeFunc(reflect.TypeOf(((func())(nil))),
|
|
func(args []reflect.Value) []reflect.Value {
|
|
c <- true
|
|
return nil
|
|
}).Interface().(func())
|
|
go g()
|
|
}
|
|
for i := 0; i < N*2; i++ {
|
|
<-c
|
|
}
|
|
}
|