diff options
Diffstat (limited to 'src/runtime/panic.go')
-rw-r--r-- | src/runtime/panic.go | 58 |
1 files changed, 33 insertions, 25 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 4b8bca6c56..e66fe27be0 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -396,39 +396,47 @@ func freedeferfn() { throw("freedefer with d.fn != nil") } -// deferreturn runs deferred functions for the caller's frame. +// Run a deferred function if there is one. // The compiler inserts a call to this at the end of any // function which calls defer. +// If there is a deferred function, this will call runtime·jmpdefer, +// which will jump to the deferred function such that it appears +// to have been called by the caller of deferreturn at the point +// just before deferreturn was called. The effect is that deferreturn +// is called again and again until there are no more deferred functions. func deferreturn() { gp := getg() - for { - d := gp._defer - if d == nil { - return - } - sp := getcallersp() - if d.sp != sp { - return - } - if d.openDefer { - done := runOpenDeferFrame(gp, d) - if !done { - throw("unfinished open-coded defers in deferreturn") - } - gp._defer = d.link - freedefer(d) - // If this frame uses open defers, then this - // must be the only defer record for the - // frame, so we can just return. - return + d := gp._defer + if d == nil { + return + } + sp := getcallersp() + if d.sp != sp { + return + } + if d.openDefer { + done := runOpenDeferFrame(gp, d) + if !done { + throw("unfinished open-coded defers in deferreturn") } - - fn := d.fn - d.fn = nil gp._defer = d.link freedefer(d) - fn() + return } + + fn := d.fn + d.fn = nil + gp._defer = d.link + freedefer(d) + // If the defer function pointer is nil, force the seg fault to happen + // here rather than in jmpdefer. gentraceback() throws an error if it is + // called with a callback on an LR architecture and jmpdefer is on the + // stack, because jmpdefer manipulates SP (see issue #8153). + _ = **(**funcval)(unsafe.Pointer(&fn)) + // We must not split the stack between computing argp and + // calling jmpdefer because argp is a uintptr stack pointer. + argp := getcallersp() + sys.MinFrameSize + jmpdefer(fn, argp) } // Goexit terminates the goroutine that calls it. No other goroutine is affected. |