diff options
Diffstat (limited to 'src/runtime/panic.go')
| -rw-r--r-- | src/runtime/panic.go | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go new file mode 100644 index 0000000000..1e35561d15 --- /dev/null +++ b/src/runtime/panic.go @@ -0,0 +1,216 @@ +// 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() +} |
