1
0
mirror of https://github.com/golang/go synced 2024-10-03 00:21:22 -06:00
go/src/runtime/panic.go

217 lines
5.9 KiB
Go
Raw Normal View History

// Copyright 2014 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.
package runtime
import "unsafe"
var indexError = error(errorString("index out of range"))
func panicindex() {
panic(indexError)
}
var sliceError = error(errorString("slice bounds out of range"))
func panicslice() {
panic(sliceError)
}
var divideError = error(errorString("integer divide by zero"))
func panicdivide() {
panic(divideError)
}
func throwreturn() {
gothrow("no return at end of a typed function - compiler is broken")
}
func throwinit() {
gothrow("recursive call during initialization - linker skew")
}
// Create a new deferred function fn with siz bytes of arguments.
// The compiler turns a defer statement into a call to this.
//go:nosplit
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
// the arguments of fn are in a perilous state. The stack map
// for deferproc does not describe them. So we can't let garbage
// collection or stack copying trigger until we've copied them out
// to somewhere safe. deferproc_m does that. Until deferproc_m,
// we can only call nosplit routines.
argp := uintptr(unsafe.Pointer(&fn))
argp += unsafe.Sizeof(fn)
if GOARCH == "arm" {
argp += ptrSize // skip caller's saved link register
}
mp := acquirem()
mp.scalararg[0] = uintptr(siz)
mp.ptrarg[0] = unsafe.Pointer(fn)
mp.scalararg[1] = argp
mp.scalararg[2] = getcallerpc(unsafe.Pointer(&siz))
if mp.curg != getg() {
// go code on the m stack can't defer
gothrow("defer on m")
}
onM(deferproc_m)
releasem(mp)
// deferproc returns 0 normally.
// a deferred func that stops a panic
// makes the deferproc return 1.
// the code the compiler generates always
// checks the return value and jumps to the
// end of the function if deferproc returns != 0.
return0()
// No code can go here - the C return register has
// been set and must not be clobbered.
}
// Each P holds pool for defers with arg sizes 8, 24, 40, 56 and 72 bytes.
// Memory block is 40 (24 for 32 bits) bytes larger due to Defer header.
// This maps exactly to malloc size classes.
// defer size class for arg size sz
func deferclass(siz uintptr) uintptr {
return (siz + 7) >> 4
}
// total size of memory block for defer with arg size sz
func totaldefersize(siz uintptr) uintptr {
return (unsafe.Sizeof(_defer{}) - unsafe.Sizeof(_defer{}.args)) + round(siz, ptrSize)
}
// Ensure that defer arg sizes that map to the same defer size class
// also map to the same malloc size class.
func testdefersizes() {
var m [len(p{}.deferpool)]int32
for i := range m {
m[i] = -1
}
for i := uintptr(0); ; i++ {
defersc := deferclass(i)
if defersc >= uintptr(len(m)) {
break
}
siz := goroundupsize(totaldefersize(i))
if m[defersc] < 0 {
m[defersc] = int32(siz)
continue
}
if m[defersc] != int32(siz) {
print("bad defer size class: i=", i, " siz=", siz, " defersc=", defersc, "\n")
gothrow("bad defer size class")
}
}
}
// Allocate a Defer, usually using per-P pool.
// Each defer must be released with freedefer.
// Note: runs on M stack
func newdefer(siz int32) *_defer {
var d *_defer
sc := deferclass(uintptr(siz))
mp := acquirem()
if sc < uintptr(len(p{}.deferpool)) {
pp := mp.p
d = pp.deferpool[sc]
if d != nil {
pp.deferpool[sc] = d.link
}
}
if d == nil {
// deferpool is empty or just a big defer
total := goroundupsize(totaldefersize(uintptr(siz)))
d = (*_defer)(gomallocgc(total, conservative, 0))
}
d.siz = siz
d.special = false
gp := mp.curg
d.link = gp._defer
gp._defer = d
releasem(mp)
return d
}
// Free the given defer.
// The defer cannot be used after this call.
func freedefer(d *_defer) {
if d.special {
return
}
sc := deferclass(uintptr(d.siz))
if sc < uintptr(len(p{}.deferpool)) {
mp := acquirem()
pp := mp.p
d.link = pp.deferpool[sc]
pp.deferpool[sc] = d
releasem(mp)
// No need to wipe out pointers in argp/pc/fn/args,
// because we empty the pool before GC.
}
}
// 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.
// Cannot split the stack because we reuse the caller's frame to
// call the deferred function.
// The single argument isn't actually used - it just has its address
// taken so it can be matched against pending defers.
//go:nosplit
func deferreturn(arg0 uintptr) {
gp := getg()
d := gp._defer
if d == nil {
return
}
argp := uintptr(unsafe.Pointer(&arg0))
if d.argp != argp {
return
}
// Moving arguments around.
// Do not allow preemption here, because the garbage collector
// won't know the form of the arguments until the jmpdefer can
// flip the PC over to fn.
mp := acquirem()
memmove(unsafe.Pointer(argp), unsafe.Pointer(&d.args), uintptr(d.siz))
fn := d.fn
gp._defer = d.link
freedefer(d)
releasem(mp)
jmpdefer(fn, argp)
}
// Goexit terminates the goroutine that calls it. No other goroutine is affected.
// Goexit runs all deferred calls before terminating the goroutine.
//
// Calling Goexit from the main goroutine terminates that goroutine
// without func main returning. Since func main has not returned,
// the program continues execution of other goroutines.
// If all other goroutines exit, the program crashes.
func Goexit() {
// Run all deferred functions for the current goroutine.
gp := getg()
for gp._defer != nil {
d := gp._defer
gp._defer = d.link
reflectcall(unsafe.Pointer(d.fn), unsafe.Pointer(&d.args), uint32(d.siz), uint32(d.siz))
freedefer(d)
// Note: we ignore recovers here because Goexit isn't a panic
}
goexit()
}