Skip to content
Snippets Groups Projects
stack.go 35.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt
    
    	// Be conservative about where we preempt.
    	// We are interested in preempting user Go code, not runtime code.
    
    	// If we're holding locks, mallocing, or preemption is disabled, don't
    	// preempt.
    
    	// This check is very early in newstack so that even the status change
    	// from Grunning to Gwaiting and back doesn't happen in this case.
    	// That status change by itself can be viewed as a small preemption,
    	// because the GC might change Gwaiting to Gscanwaiting, and then
    	// this goroutine has to wait for the GC to finish before continuing.
    	// If the GC is in some way dependent on this goroutine (for example,
    	// it needs a lock held by the goroutine), that small preemption turns
    	// into a real deadlock.
    
    		if thisg.m.locks != 0 || thisg.m.mallocing != 0 || thisg.m.preemptoff != "" || thisg.m.p.ptr().status != _Prunning {
    
    			// Let the goroutine keep running for now.
    			// gp->preempt is set, so it will be preempted next time.
    			gp.stackguard0 = gp.stack.lo + _StackGuard
    			gogo(&gp.sched) // never return
    		}
    	}
    
    		throw("missing stack in newstack")
    
    	if sys.ArchFamily == sys.AMD64 || sys.ArchFamily == sys.I386 {
    
    		// The call to morestack cost a word.
    
    	}
    	if stackDebug >= 1 || sp < gp.stack.lo {
    		print("runtime: newstack sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n",
    			"\tmorebuf={pc:", hex(morebuf.pc), " sp:", hex(morebuf.sp), " lr:", hex(morebuf.lr), "}\n",
    			"\tsched={pc:", hex(gp.sched.pc), " sp:", hex(gp.sched.sp), " lr:", hex(gp.sched.lr), " ctxt:", gp.sched.ctxt, "}\n")
    	}
    	if sp < gp.stack.lo {
    		print("runtime: gp=", gp, ", gp->status=", hex(readgstatus(gp)), "\n ")
    		print("runtime: split stack overflow: ", hex(sp), " < ", hex(gp.stack.lo), "\n")
    
    		throw("runtime: split stack overflow")
    
    			throw("runtime: preempt g0")
    
    		if thisg.m.p == 0 && thisg.m.locks == 0 {
    
    			throw("runtime: g is running but p is not")
    
    		// Synchronize with scang.
    		casgstatus(gp, _Grunning, _Gwaiting)
    
    			for !castogscanstatus(gp, _Gwaiting, _Gscanwaiting) {
    
    				// Likely to be racing with the GC as
    				// it sees a _Gwaiting and does the
    				// stack scan. If so, gcworkdone will
    				// be set and gcphasework will simply
    				// return.
    
    			if !gp.gcscandone {
    
    				// gcw is safe because we're on the
    				// system stack.
    				gcw := &gp.m.p.ptr().gcw
    				scanstack(gp, gcw)
    				if gcBlackenPromptly {
    					gcw.dispose()
    				}
    
    				gp.gcscandone = true
    			}
    			gp.preemptscan = false
    			gp.preempt = false
    
    			casfrom_Gscanstatus(gp, _Gscanwaiting, _Gwaiting)
    
    			// This clears gcscanvalid.
    
    			casgstatus(gp, _Gwaiting, _Grunning)
    
    			gp.stackguard0 = gp.stack.lo + _StackGuard
    
    			gogo(&gp.sched) // never return
    
    		}
    
    		// Act like goroutine called runtime.Gosched.
    		casgstatus(gp, _Gwaiting, _Grunning)
    
    		gopreempt_m(gp) // never return
    
    	}
    
    	// Allocate a bigger segment and move the stack.
    
    	oldsize := int(gp.stackAlloc)
    
    	newsize := oldsize * 2
    	if uintptr(newsize) > maxstacksize {
    		print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n")
    
    		throw("stack overflow")
    
    	// The goroutine must be executing in order to call newstack,
    	// so it must be Grunning (or Gscanrunning).
    	casgstatus(gp, _Grunning, _Gcopystack)
    
    
    	// The concurrent GC will not scan the stack while we are doing the copy since
    	// the gp is in a Gcopystack status.
    
    	copystack(gp, uintptr(newsize), true)
    
    	if stackDebug >= 1 {
    		print("stack grow done\n")
    	}
    
    	casgstatus(gp, _Gcopystack, _Grunning)
    
    	gogo(&gp.sched)
    }
    
    //go:nosplit
    func nilfunc() {
    	*(*uint8)(nil) = 0
    }
    
    // adjust Gobuf as if it executed a call to fn
    // and then did an immediate gosave.
    func gostartcallfn(gobuf *gobuf, fv *funcval) {
    	var fn unsafe.Pointer
    	if fv != nil {
    
    	} else {
    		fn = unsafe.Pointer(funcPC(nilfunc))
    	}
    
    	gostartcall(gobuf, fn, unsafe.Pointer(fv))
    
    }
    
    // Maybe shrink the stack being used by gp.
    // Called at garbage collection time.
    
    // gp must be stopped, but the world need not be.
    
    func shrinkstack(gp *g) {
    
    	gstatus := readgstatus(gp)
    	if gstatus&^_Gscan == _Gdead {
    
    		if gp.stack.lo != 0 {
    			// Free whole stack - it will get reallocated
    			// if G is used again.
    
    			stackfree(gp.stack, gp.stackAlloc)
    
    			gp.stack.lo = 0
    			gp.stack.hi = 0
    
    			gp.stkbar = nil
    			gp.stkbarPos = 0
    
    		throw("missing stack in shrinkstack")
    
    	if gstatus&_Gscan == 0 {
    		throw("bad status in shrinkstack")
    	}
    
    	if gp.startpc == gcBgMarkWorkerPC {
    		// We're not allowed to shrink the gcBgMarkWorker
    		// stack (see gcBgMarkWorker for explanation).
    		return
    	}
    
    	// Don't shrink the allocation below the minimum-sized stack
    	// allocation.
    
    	if newsize < _FixedStack {
    
    	// Compute how much of the stack is currently in use and only
    	// shrink the stack if gp is using less than a quarter of its
    	// current stack. The currently used stack includes everything
    	// down to the SP plus the stack guard space that ensures
    	// there's room for nosplit functions.
    	avail := gp.stack.hi - gp.stack.lo
    	if used := gp.stack.hi - gp.sched.sp + _StackLimit; used >= avail/4 {
    		return
    
    	}
    
    	// We can't copy the stack if we're in a syscall.
    	// The syscall might have pointers into the stack.
    	if gp.syscallsp != 0 {
    		return
    	}
    
    	if sys.GoosWindows != 0 && gp.m != nil && gp.m.libcallsp != 0 {
    
    		return
    	}
    
    	if stackDebug > 0 {
    		print("shrinking stack ", oldsize, "->", newsize, "\n")
    	}
    
    	copystack(gp, newsize, false)
    
    // freeStackSpans frees unused stack spans at the end of GC.
    func freeStackSpans() {
    
    
    	// Scan stack pools for empty stack spans.
    	for order := range stackpool {
    		list := &stackpool[order]
    
    		for s := list.first; s != nil; {
    
    			if s.allocCount == 0 {
    
    				s.stackfreelist = 0
    
    
    	// Free large stack spans.
    	lock(&stackLarge.lock)
    	for i := range stackLarge.free {
    		for s := stackLarge.free[i].first; s != nil; {
    			next := s.next
    			stackLarge.free[i].remove(s)
    			mheap_.freeStack(s)
    			s = next
    		}
    	}
    	unlock(&stackLarge.lock)
    
    
    //go:nosplit
    func morestackc() {
    	systemstack(func() {
    		throw("attempt to execute C code on Go stack")
    	})
    }
    
    
    //go:nosplit
    func inStack(p uintptr, s stack) bool {
    	return s.lo <= p && p < s.hi
    }