===================================================================
@@ -1,4 +1,4 @@
-fc0cfdff94ca1099421900f43837ca5a70189cd6
+0a20181d00d43a423c55f4e772b759fba0619478
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
===================================================================
@@ -95,9 +95,34 @@ func CgocallBack() {
// CgocallBackDone prepares to return to C/C++ code that has called
// into Go code.
func CgocallBackDone() {
+ // If we are the top level Go function called from C/C++, then
+ // we need to release the m. But don't release it if we are
+ // panicing; since this is the top level, we are going to
+ // crash the program, and we need the g and m to print the
+ // panic values.
+ //
+ // Dropping the m is going to clear g. This function is being
+ // called as a deferred function, so we will return to
+ // deferreturn which will want to clear the _defer field.
+ // As soon as we call dropm another thread may call needm and
+ // start using g, so we must not tamper with the _defer field
+ // after dropm. So clear _defer now.
+ gp := getg()
+ mp := gp.m
+ drop := false
+ if mp.dropextram && mp.ncgo == 0 && gp._panic == nil {
+ d := gp._defer
+ if d == nil || d.link != nil {
+ throw("unexpected g._defer in CgocallBackDone")
+ }
+ gp._defer = nil
+ freedefer(d)
+ drop = true
+ }
+
entersyscall(0)
- mp := getg().m
- if mp.dropextram && mp.ncgo == 0 {
+
+ if drop {
mp.dropextram = false
dropm()
}
===================================================================
@@ -143,14 +143,6 @@ func newdefer() *_defer {
//
//go:nosplit
func freedefer(d *_defer) {
- // When C code calls a Go function on a non-Go thread, the
- // deferred call to cgocallBackDone will set g to nil.
- // Don't crash trying to put d on the free list; just let it
- // be garbage collected.
- if getg() == nil {
- return
- }
-
pp := getg().m.p.ptr()
if len(pp.deferpool) == cap(pp.deferpool) {
// Transfer half of local cache to the central cache.
@@ -201,6 +193,15 @@ func deferreturn(frame *bool) {
fn(d.arg)
}
+ // If we are returning from a Go function called by a
+ // C function running in a C thread, g may now be nil,
+ // in which case CgocallBackDone will have cleared _defer.
+ // In that case some other goroutine may already be using gp.
+ if getg() == nil {
+ *frame = true
+ return
+ }
+
gp._defer = d.link
freedefer(d)