]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.cc] all: merge default (8d42099cdc23) into dev.cc
authorRuss Cox <rsc@golang.org>
Fri, 5 Dec 2014 16:18:10 +0000 (11:18 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 5 Dec 2014 16:18:10 +0000 (11:18 -0500)
TBR=austin
CC=golang-codereviews
https://golang.org/cl/178700044

1  2 
src/go/build/build.go
src/reflect/all_test.go
src/reflect/type.go
src/runtime/proc1.go
src/runtime/stack1.go

Simple merge
Simple merge
Simple merge
index 81b211d0d35039a0b5c4099a04a63722ec1c326f,0000000000000000000000000000000000000000..aeded0e77308861bd8f02ff481a5f2ba72bcd3de
mode 100644,000000..100644
--- /dev/null
@@@ -1,3170 -1,0 +1,3193 @@@
 +// Copyright 2009 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 (
 +      m0 m
 +      g0 g
 +)
 +
 +// Goroutine scheduler
 +// The scheduler's job is to distribute ready-to-run goroutines over worker threads.
 +//
 +// The main concepts are:
 +// G - goroutine.
 +// M - worker thread, or machine.
 +// P - processor, a resource that is required to execute Go code.
 +//     M must have an associated P to execute Go code, however it can be
 +//     blocked or in a syscall w/o an associated P.
 +//
 +// Design doc at http://golang.org/s/go11sched.
 +
 +const (
 +      // Number of goroutine ids to grab from sched.goidgen to local per-P cache at once.
 +      // 16 seems to provide enough amortization, but other than that it's mostly arbitrary number.
 +      _GoidCacheBatch = 16
 +)
 +
 +/*
 +SchedT        sched;
 +int32 gomaxprocs;
 +uint32        needextram;
 +bool  iscgo;
 +M     m0;
 +G     g0;     // idle goroutine for m0
 +G*    lastg;
 +M*    allm;
 +M*    extram;
 +P*    allp[MaxGomaxprocs+1];
 +int8* goos;
 +int32 ncpu;
 +int32 newprocs;
 +
 +Mutex allglock;       // the following vars are protected by this lock or by stoptheworld
 +G**   allg;
 +Slice allgs;
 +uintptr allglen;
 +ForceGCState  forcegc;
 +
 +void mstart(void);
 +static void runqput(P*, G*);
 +static G* runqget(P*);
 +static bool runqputslow(P*, G*, uint32, uint32);
 +static G* runqsteal(P*, P*);
 +static void mput(M*);
 +static M* mget(void);
 +static void mcommoninit(M*);
 +static void schedule(void);
 +static void procresize(int32);
 +static void acquirep(P*);
 +static P* releasep(void);
 +static void newm(void(*)(void), P*);
 +static void stopm(void);
 +static void startm(P*, bool);
 +static void handoffp(P*);
 +static void wakep(void);
 +static void stoplockedm(void);
 +static void startlockedm(G*);
 +static void sysmon(void);
 +static uint32 retake(int64);
 +static void incidlelocked(int32);
 +static void checkdead(void);
 +static void exitsyscall0(G*);
 +void park_m(G*);
 +static void goexit0(G*);
 +static void gfput(P*, G*);
 +static G* gfget(P*);
 +static void gfpurge(P*);
 +static void globrunqput(G*);
 +static void globrunqputbatch(G*, G*, int32);
 +static G* globrunqget(P*, int32);
 +static P* pidleget(void);
 +static void pidleput(P*);
 +static void injectglist(G*);
 +static bool preemptall(void);
 +static bool preemptone(P*);
 +static bool exitsyscallfast(void);
 +static bool haveexperiment(int8*);
 +void allgadd(G*);
 +static void dropg(void);
 +
 +extern String buildVersion;
 +*/
 +
 +// The bootstrap sequence is:
 +//
 +//    call osinit
 +//    call schedinit
 +//    make & queue new G
 +//    call runtime·mstart
 +//
 +// The new G calls runtime·main.
 +func schedinit() {
 +      // raceinit must be the first call to race detector.
 +      // In particular, it must be done before mallocinit below calls racemapshadow.
 +      _g_ := getg()
 +      if raceenabled {
 +              _g_.racectx = raceinit()
 +      }
 +
 +      sched.maxmcount = 10000
 +
 +      tracebackinit()
 +      symtabinit()
 +      stackinit()
 +      mallocinit()
 +      mcommoninit(_g_.m)
 +
 +      goargs()
 +      goenvs()
 +      parsedebugvars()
 +      gcinit()
 +
 +      sched.lastpoll = uint64(nanotime())
 +      procs := 1
 +      if n := goatoi(gogetenv("GOMAXPROCS")); n > 0 {
 +              if n > _MaxGomaxprocs {
 +                      n = _MaxGomaxprocs
 +              }
 +              procs = n
 +      }
 +      procresize(int32(procs))
 +
 +      if buildVersion == "" {
 +              // Condition should never trigger.  This code just serves
 +              // to ensure runtime·buildVersion is kept in the resulting binary.
 +              buildVersion = "unknown"
 +      }
 +}
 +
 +func newsysmon() {
 +      _newm(sysmon, nil)
 +}
 +
 +func dumpgstatus(gp *g) {
 +      _g_ := getg()
 +      print("runtime: gp: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n")
 +      print("runtime:  g:  g=", _g_, ", goid=", _g_.goid, ",  g->atomicstatus=", readgstatus(_g_), "\n")
 +}
 +
 +func checkmcount() {
 +      // sched lock is held
 +      if sched.mcount > sched.maxmcount {
 +              print("runtime: program exceeds ", sched.maxmcount, "-thread limit\n")
 +              gothrow("thread exhaustion")
 +      }
 +}
 +
 +func mcommoninit(mp *m) {
 +      _g_ := getg()
 +
 +      // g0 stack won't make sense for user (and is not necessary unwindable).
 +      if _g_ != _g_.m.g0 {
 +              callers(1, &mp.createstack[0], len(mp.createstack))
 +      }
 +
 +      mp.fastrand = 0x49f6428a + uint32(mp.id) + uint32(cputicks())
 +      if mp.fastrand == 0 {
 +              mp.fastrand = 0x49f6428a
 +      }
 +
 +      lock(&sched.lock)
 +      mp.id = sched.mcount
 +      sched.mcount++
 +      checkmcount()
 +      mpreinit(mp)
 +      if mp.gsignal != nil {
 +              mp.gsignal.stackguard1 = mp.gsignal.stack.lo + _StackGuard
 +      }
 +
 +      // Add to allm so garbage collector doesn't free g->m
 +      // when it is just in a register or thread-local storage.
 +      mp.alllink = allm
 +
 +      // NumCgoCall() iterates over allm w/o schedlock,
 +      // so we need to publish it safely.
 +      atomicstorep(unsafe.Pointer(&allm), unsafe.Pointer(mp))
 +      unlock(&sched.lock)
 +}
 +
 +// Mark gp ready to run.
 +func ready(gp *g) {
 +      status := readgstatus(gp)
 +
 +      // Mark runnable.
 +      _g_ := getg()
 +      _g_.m.locks++ // disable preemption because it can be holding p in a local var
 +      if status&^_Gscan != _Gwaiting {
 +              dumpgstatus(gp)
 +              gothrow("bad g->status in ready")
 +      }
 +
 +      // status is Gwaiting or Gscanwaiting, make Grunnable and put on runq
 +      casgstatus(gp, _Gwaiting, _Grunnable)
 +      runqput(_g_.m.p, gp)
 +      if atomicload(&sched.npidle) != 0 && atomicload(&sched.nmspinning) == 0 { // TODO: fast atomic
 +              wakep()
 +      }
 +      _g_.m.locks--
 +      if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack
 +              _g_.stackguard0 = stackPreempt
 +      }
 +}
 +
 +func gcprocs() int32 {
 +      // Figure out how many CPUs to use during GC.
 +      // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
 +      lock(&sched.lock)
 +      n := gomaxprocs
 +      if n > ncpu {
 +              n = ncpu
 +      }
 +      if n > _MaxGcproc {
 +              n = _MaxGcproc
 +      }
 +      if n > sched.nmidle+1 { // one M is currently running
 +              n = sched.nmidle + 1
 +      }
 +      unlock(&sched.lock)
 +      return n
 +}
 +
 +func needaddgcproc() bool {
 +      lock(&sched.lock)
 +      n := gomaxprocs
 +      if n > ncpu {
 +              n = ncpu
 +      }
 +      if n > _MaxGcproc {
 +              n = _MaxGcproc
 +      }
 +      n -= sched.nmidle + 1 // one M is currently running
 +      unlock(&sched.lock)
 +      return n > 0
 +}
 +
 +func helpgc(nproc int32) {
 +      _g_ := getg()
 +      lock(&sched.lock)
 +      pos := 0
 +      for n := int32(1); n < nproc; n++ { // one M is currently running
 +              if allp[pos].mcache == _g_.m.mcache {
 +                      pos++
 +              }
 +              mp := mget()
 +              if mp == nil {
 +                      gothrow("gcprocs inconsistency")
 +              }
 +              mp.helpgc = n
 +              mp.mcache = allp[pos].mcache
 +              pos++
 +              notewakeup(&mp.park)
 +      }
 +      unlock(&sched.lock)
 +}
 +
 +// Similar to stoptheworld but best-effort and can be called several times.
 +// There is no reverse operation, used during crashing.
 +// This function must not lock any mutexes.
 +func freezetheworld() {
 +      if gomaxprocs == 1 {
 +              return
 +      }
 +      // stopwait and preemption requests can be lost
 +      // due to races with concurrently executing threads,
 +      // so try several times
 +      for i := 0; i < 5; i++ {
 +              // this should tell the scheduler to not start any new goroutines
 +              sched.stopwait = 0x7fffffff
 +              atomicstore(&sched.gcwaiting, 1)
 +              // this should stop running goroutines
 +              if !preemptall() {
 +                      break // no running goroutines
 +              }
 +              usleep(1000)
 +      }
 +      // to be sure
 +      usleep(1000)
 +      preemptall()
 +      usleep(1000)
 +}
 +
 +func isscanstatus(status uint32) bool {
 +      if status == _Gscan {
 +              gothrow("isscanstatus: Bad status Gscan")
 +      }
 +      return status&_Gscan == _Gscan
 +}
 +
 +// All reads and writes of g's status go through readgstatus, casgstatus
 +// castogscanstatus, casfrom_Gscanstatus.
 +//go:nosplit
 +func readgstatus(gp *g) uint32 {
 +      return atomicload(&gp.atomicstatus)
 +}
 +
 +// The Gscanstatuses are acting like locks and this releases them.
 +// If it proves to be a performance hit we should be able to make these
 +// simple atomic stores but for now we are going to throw if
 +// we see an inconsistent state.
 +func casfrom_Gscanstatus(gp *g, oldval, newval uint32) {
 +      success := false
 +
 +      // Check that transition is valid.
 +      switch oldval {
 +      case _Gscanrunnable,
 +              _Gscanwaiting,
 +              _Gscanrunning,
 +              _Gscansyscall:
 +              if newval == oldval&^_Gscan {
 +                      success = cas(&gp.atomicstatus, oldval, newval)
 +              }
 +      case _Gscanenqueue:
 +              if newval == _Gwaiting {
 +                      success = cas(&gp.atomicstatus, oldval, newval)
 +              }
 +      }
 +      if !success {
 +              print("runtime: casfrom_Gscanstatus failed gp=", gp, ", oldval=", hex(oldval), ", newval=", hex(newval), "\n")
 +              dumpgstatus(gp)
 +              gothrow("casfrom_Gscanstatus: gp->status is not in scan state")
 +      }
 +}
 +
 +// This will return false if the gp is not in the expected status and the cas fails.
 +// This acts like a lock acquire while the casfromgstatus acts like a lock release.
 +func castogscanstatus(gp *g, oldval, newval uint32) bool {
 +      switch oldval {
 +      case _Grunnable,
 +              _Gwaiting,
 +              _Gsyscall:
 +              if newval == oldval|_Gscan {
 +                      return cas(&gp.atomicstatus, oldval, newval)
 +              }
 +      case _Grunning:
 +              if newval == _Gscanrunning || newval == _Gscanenqueue {
 +                      return cas(&gp.atomicstatus, oldval, newval)
 +              }
 +      }
 +      print("runtime: castogscanstatus oldval=", hex(oldval), " newval=", hex(newval), "\n")
 +      gothrow("castogscanstatus")
 +      panic("not reached")
 +}
 +
 +// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
 +// and casfrom_Gscanstatus instead.
 +// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
 +// put it in the Gscan state is finished.
 +//go:nosplit
 +func casgstatus(gp *g, oldval, newval uint32) {
 +      if (oldval&_Gscan != 0) || (newval&_Gscan != 0) || oldval == newval {
 +              systemstack(func() {
 +                      print("casgstatus: oldval=", hex(oldval), " newval=", hex(newval), "\n")
 +                      gothrow("casgstatus: bad incoming values")
 +              })
 +      }
 +
 +      // loop if gp->atomicstatus is in a scan state giving
 +      // GC time to finish and change the state to oldval.
 +      for !cas(&gp.atomicstatus, oldval, newval) {
++              if oldval == _Gwaiting && gp.atomicstatus == _Grunnable {
++                      systemstack(func() {
++                              gothrow("casgstatus: waiting for Gwaiting but is Grunnable")
++                      })
++              }
 +              // Help GC if needed.
 +              if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) {
 +                      gp.preemptscan = false
 +                      systemstack(func() {
 +                              gcphasework(gp)
 +                      })
 +              }
 +      }
 +}
 +
++// casgstatus(gp, oldstatus, Gcopystack), assuming oldstatus is Gwaiting or Grunnable.
++// Returns old status. Cannot call casgstatus directly, because we are racing with an
++// async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus,
++// it might have become Grunnable by the time we get to the cas. If we called casgstatus,
++// it would loop waiting for the status to go back to Gwaiting, which it never will.
++//go:nosplit
++func casgcopystack(gp *g) uint32 {
++      for {
++              oldstatus := readgstatus(gp) &^ _Gscan
++              if oldstatus != _Gwaiting && oldstatus != _Grunnable {
++                      gothrow("copystack: bad status, not Gwaiting or Grunnable")
++              }
++              if cas(&gp.atomicstatus, oldstatus, _Gcopystack) {
++                      return oldstatus
++              }
++      }
++}
++
 +// stopg ensures that gp is stopped at a GC safe point where its stack can be scanned
 +// or in the context of a moving collector the pointers can be flipped from pointing
 +// to old object to pointing to new objects.
 +// If stopg returns true, the caller knows gp is at a GC safe point and will remain there until
 +// the caller calls restartg.
 +// If stopg returns false, the caller is not responsible for calling restartg. This can happen
 +// if another thread, either the gp itself or another GC thread is taking the responsibility
 +// to do the GC work related to this thread.
 +func stopg(gp *g) bool {
 +      for {
 +              if gp.gcworkdone {
 +                      return false
 +              }
 +
 +              switch s := readgstatus(gp); s {
 +              default:
 +                      dumpgstatus(gp)
 +                      gothrow("stopg: gp->atomicstatus is not valid")
 +
 +              case _Gdead:
 +                      return false
 +
 +              case _Gcopystack:
 +                      // Loop until a new stack is in place.
 +
 +              case _Grunnable,
 +                      _Gsyscall,
 +                      _Gwaiting:
 +                      // Claim goroutine by setting scan bit.
 +                      if !castogscanstatus(gp, s, s|_Gscan) {
 +                              break
 +                      }
 +                      // In scan state, do work.
 +                      gcphasework(gp)
 +                      return true
 +
 +              case _Gscanrunnable,
 +                      _Gscanwaiting,
 +                      _Gscansyscall:
 +                      // Goroutine already claimed by another GC helper.
 +                      return false
 +
 +              case _Grunning:
 +                      // Claim goroutine, so we aren't racing with a status
 +                      // transition away from Grunning.
 +                      if !castogscanstatus(gp, _Grunning, _Gscanrunning) {
 +                              break
 +                      }
 +
 +                      // Mark gp for preemption.
 +                      if !gp.gcworkdone {
 +                              gp.preemptscan = true
 +                              gp.preempt = true
 +                              gp.stackguard0 = stackPreempt
 +                      }
 +
 +                      // Unclaim.
 +                      casfrom_Gscanstatus(gp, _Gscanrunning, _Grunning)
 +                      return false
 +              }
 +      }
 +}
 +
 +// The GC requests that this routine be moved from a scanmumble state to a mumble state.
 +func restartg(gp *g) {
 +      s := readgstatus(gp)
 +      switch s {
 +      default:
 +              dumpgstatus(gp)
 +              gothrow("restartg: unexpected status")
 +
 +      case _Gdead:
 +              // ok
 +
 +      case _Gscanrunnable,
 +              _Gscanwaiting,
 +              _Gscansyscall:
 +              casfrom_Gscanstatus(gp, s, s&^_Gscan)
 +
 +      // Scan is now completed.
 +      // Goroutine now needs to be made runnable.
 +      // We put it on the global run queue; ready blocks on the global scheduler lock.
 +      case _Gscanenqueue:
 +              casfrom_Gscanstatus(gp, _Gscanenqueue, _Gwaiting)
 +              if gp != getg().m.curg {
 +                      gothrow("processing Gscanenqueue on wrong m")
 +              }
 +              dropg()
 +              ready(gp)
 +      }
 +}
 +
 +func stopscanstart(gp *g) {
 +      _g_ := getg()
 +      if _g_ == gp {
 +              gothrow("GC not moved to G0")
 +      }
 +      if stopg(gp) {
 +              if !isscanstatus(readgstatus(gp)) {
 +                      dumpgstatus(gp)
 +                      gothrow("GC not in scan state")
 +              }
 +              restartg(gp)
 +      }
 +}
 +
 +// Runs on g0 and does the actual work after putting the g back on the run queue.
 +func mquiesce(gpmaster *g) {
 +      activeglen := len(allgs)
 +      // enqueue the calling goroutine.
 +      restartg(gpmaster)
 +      for i := 0; i < activeglen; i++ {
 +              gp := allgs[i]
 +              if readgstatus(gp) == _Gdead {
 +                      gp.gcworkdone = true // noop scan.
 +              } else {
 +                      gp.gcworkdone = false
 +              }
 +              stopscanstart(gp)
 +      }
 +
 +      // Check that the G's gcwork (such as scanning) has been done. If not do it now.
 +      // You can end up doing work here if the page trap on a Grunning Goroutine has
 +      // not been sprung or in some race situations. For example a runnable goes dead
 +      // and is started up again with a gp->gcworkdone set to false.
 +      for i := 0; i < activeglen; i++ {
 +              gp := allgs[i]
 +              for !gp.gcworkdone {
 +                      status := readgstatus(gp)
 +                      if status == _Gdead {
 +                              //do nothing, scan not needed.
 +                              gp.gcworkdone = true // scan is a noop
 +                              break
 +                      }
 +                      if status == _Grunning && gp.stackguard0 == uintptr(stackPreempt) && notetsleep(&sched.stopnote, 100*1000) { // nanosecond arg
 +                              noteclear(&sched.stopnote)
 +                      } else {
 +                              stopscanstart(gp)
 +                      }
 +              }
 +      }
 +
 +      for i := 0; i < activeglen; i++ {
 +              gp := allgs[i]
 +              status := readgstatus(gp)
 +              if isscanstatus(status) {
 +                      print("mstopandscang:bottom: post scan bad status gp=", gp, " has status ", hex(status), "\n")
 +                      dumpgstatus(gp)
 +              }
 +              if !gp.gcworkdone && status != _Gdead {
 +                      print("mstopandscang:bottom: post scan gp=", gp, "->gcworkdone still false\n")
 +                      dumpgstatus(gp)
 +              }
 +      }
 +
 +      schedule() // Never returns.
 +}
 +
 +// quiesce moves all the goroutines to a GC safepoint which for now is a at preemption point.
 +// If the global gcphase is GCmark quiesce will ensure that all of the goroutine's stacks
 +// have been scanned before it returns.
 +func quiesce(mastergp *g) {
 +      castogscanstatus(mastergp, _Grunning, _Gscanenqueue)
 +      // Now move this to the g0 (aka m) stack.
 +      // g0 will potentially scan this thread and put mastergp on the runqueue
 +      mcall(mquiesce)
 +}
 +
 +// This is used by the GC as well as the routines that do stack dumps. In the case
 +// of GC all the routines can be reliably stopped. This is not always the case
 +// when the system is in panic or being exited.
 +func stoptheworld() {
 +      _g_ := getg()
 +
 +      // If we hold a lock, then we won't be able to stop another M
 +      // that is blocked trying to acquire the lock.
 +      if _g_.m.locks > 0 {
 +              gothrow("stoptheworld: holding locks")
 +      }
 +
 +      lock(&sched.lock)
 +      sched.stopwait = gomaxprocs
 +      atomicstore(&sched.gcwaiting, 1)
 +      preemptall()
 +      // stop current P
 +      _g_.m.p.status = _Pgcstop // Pgcstop is only diagnostic.
 +      sched.stopwait--
 +      // try to retake all P's in Psyscall status
 +      for i := 0; i < int(gomaxprocs); i++ {
 +              p := allp[i]
 +              s := p.status
 +              if s == _Psyscall && cas(&p.status, s, _Pgcstop) {
 +                      sched.stopwait--
 +              }
 +      }
 +      // stop idle P's
 +      for {
 +              p := pidleget()
 +              if p == nil {
 +                      break
 +              }
 +              p.status = _Pgcstop
 +              sched.stopwait--
 +      }
 +      wait := sched.stopwait > 0
 +      unlock(&sched.lock)
 +
 +      // wait for remaining P's to stop voluntarily
 +      if wait {
 +              for {
 +                      // wait for 100us, then try to re-preempt in case of any races
 +                      if notetsleep(&sched.stopnote, 100*1000) {
 +                              noteclear(&sched.stopnote)
 +                              break
 +                      }
 +                      preemptall()
 +              }
 +      }
 +      if sched.stopwait != 0 {
 +              gothrow("stoptheworld: not stopped")
 +      }
 +      for i := 0; i < int(gomaxprocs); i++ {
 +              p := allp[i]
 +              if p.status != _Pgcstop {
 +                      gothrow("stoptheworld: not stopped")
 +              }
 +      }
 +}
 +
 +func mhelpgc() {
 +      _g_ := getg()
 +      _g_.m.helpgc = -1
 +}
 +
 +func starttheworld() {
 +      _g_ := getg()
 +
 +      _g_.m.locks++        // disable preemption because it can be holding p in a local var
 +      gp := netpoll(false) // non-blocking
 +      injectglist(gp)
 +      add := needaddgcproc()
 +      lock(&sched.lock)
 +      if newprocs != 0 {
 +              procresize(newprocs)
 +              newprocs = 0
 +      } else {
 +              procresize(gomaxprocs)
 +      }
 +      sched.gcwaiting = 0
 +
 +      var p1 *p
 +      for {
 +              p := pidleget()
 +              if p == nil {
 +                      break
 +              }
 +              // procresize() puts p's with work at the beginning of the list.
 +              // Once we reach a p without a run queue, the rest don't have one either.
 +              if p.runqhead == p.runqtail {
 +                      pidleput(p)
 +                      break
 +              }
 +              p.m = mget()
 +              p.link = p1
 +              p1 = p
 +      }
 +      if sched.sysmonwait != 0 {
 +              sched.sysmonwait = 0
 +              notewakeup(&sched.sysmonnote)
 +      }
 +      unlock(&sched.lock)
 +
 +      for p1 != nil {
 +              p := p1
 +              p1 = p1.link
 +              if p.m != nil {
 +                      mp := p.m
 +                      p.m = nil
 +                      if mp.nextp != nil {
 +                              gothrow("starttheworld: inconsistent mp->nextp")
 +                      }
 +                      mp.nextp = p
 +                      notewakeup(&mp.park)
 +              } else {
 +                      // Start M to run P.  Do not start another M below.
 +                      _newm(nil, p)
 +                      add = false
 +              }
 +      }
 +
 +      if add {
 +              // If GC could have used another helper proc, start one now,
 +              // in the hope that it will be available next time.
 +              // It would have been even better to start it before the collection,
 +              // but doing so requires allocating memory, so it's tricky to
 +              // coordinate.  This lazy approach works out in practice:
 +              // we don't mind if the first couple gc rounds don't have quite
 +              // the maximum number of procs.
 +              _newm(mhelpgc, nil)
 +      }
 +      _g_.m.locks--
 +      if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack
 +              _g_.stackguard0 = stackPreempt
 +      }
 +}
 +
 +// Called to start an M.
 +//go:nosplit
 +func mstart() {
 +      _g_ := getg()
 +
 +      if _g_.stack.lo == 0 {
 +              // Initialize stack bounds from system stack.
 +              // Cgo may have left stack size in stack.hi.
 +              size := _g_.stack.hi
 +              if size == 0 {
 +                      size = 8192
 +              }
 +              _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&size)))
 +              _g_.stack.lo = _g_.stack.hi - size + 1024
 +      }
 +      // Initialize stack guards so that we can start calling
 +      // both Go and C functions with stack growth prologues.
 +      _g_.stackguard0 = _g_.stack.lo + _StackGuard
 +      _g_.stackguard1 = _g_.stackguard0
 +      mstart1()
 +}
 +
 +func mstart1() {
 +      _g_ := getg()
 +
 +      if _g_ != _g_.m.g0 {
 +              gothrow("bad runtime·mstart")
 +      }
 +
 +      // Record top of stack for use by mcall.
 +      // Once we call schedule we're never coming back,
 +      // so other calls can reuse this stack space.
 +      gosave(&_g_.m.g0.sched)
 +      _g_.m.g0.sched.pc = ^uintptr(0) // make sure it is never used
 +      asminit()
 +      minit()
 +
 +      // Install signal handlers; after minit so that minit can
 +      // prepare the thread to be able to handle the signals.
 +      if _g_.m == &m0 {
 +              initsig()
 +      }
 +
 +      if _g_.m.mstartfn != nil {
 +              fn := *(*func())(unsafe.Pointer(&_g_.m.mstartfn))
 +              fn()
 +      }
 +
 +      if _g_.m.helpgc != 0 {
 +              _g_.m.helpgc = 0
 +              stopm()
 +      } else if _g_.m != &m0 {
 +              acquirep(_g_.m.nextp)
 +              _g_.m.nextp = nil
 +      }
 +      schedule()
 +
 +      // TODO(brainman): This point is never reached, because scheduler
 +      // does not release os threads at the moment. But once this path
 +      // is enabled, we must remove our seh here.
 +}
 +
 +// When running with cgo, we call _cgo_thread_start
 +// to start threads for us so that we can play nicely with
 +// foreign code.
 +var cgoThreadStart unsafe.Pointer
 +
 +type cgothreadstart struct {
 +      g   *g
 +      tls *uint64
 +      fn  unsafe.Pointer
 +}
 +
 +// Allocate a new m unassociated with any thread.
 +// Can use p for allocation context if needed.
 +func allocm(_p_ *p) *m {
 +      _g_ := getg()
 +      _g_.m.locks++ // disable GC because it can be called from sysmon
 +      if _g_.m.p == nil {
 +              acquirep(_p_) // temporarily borrow p for mallocs in this function
 +      }
 +      mp := newM()
 +      mcommoninit(mp)
 +
 +      // In case of cgo or Solaris, pthread_create will make us a stack.
 +      // Windows and Plan 9 will layout sched stack on OS stack.
 +      if iscgo || GOOS == "solaris" || GOOS == "windows" || GOOS == "plan9" {
 +              mp.g0 = malg(-1)
 +      } else {
 +              mp.g0 = malg(8192)
 +      }
 +      mp.g0.m = mp
 +
 +      if _p_ == _g_.m.p {
 +              releasep()
 +      }
 +      _g_.m.locks--
 +      if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack
 +              _g_.stackguard0 = stackPreempt
 +      }
 +
 +      return mp
 +}
 +
 +func allocg() *g {
 +      return newG()
 +}
 +
 +// needm is called when a cgo callback happens on a
 +// thread without an m (a thread not created by Go).
 +// In this case, needm is expected to find an m to use
 +// and return with m, g initialized correctly.
 +// Since m and g are not set now (likely nil, but see below)
 +// needm is limited in what routines it can call. In particular
 +// it can only call nosplit functions (textflag 7) and cannot
 +// do any scheduling that requires an m.
 +//
 +// In order to avoid needing heavy lifting here, we adopt
 +// the following strategy: there is a stack of available m's
 +// that can be stolen. Using compare-and-swap
 +// to pop from the stack has ABA races, so we simulate
 +// a lock by doing an exchange (via casp) to steal the stack
 +// head and replace the top pointer with MLOCKED (1).
 +// This serves as a simple spin lock that we can use even
 +// without an m. The thread that locks the stack in this way
 +// unlocks the stack by storing a valid stack head pointer.
 +//
 +// In order to make sure that there is always an m structure
 +// available to be stolen, we maintain the invariant that there
 +// is always one more than needed. At the beginning of the
 +// program (if cgo is in use) the list is seeded with a single m.
 +// If needm finds that it has taken the last m off the list, its job
 +// is - once it has installed its own m so that it can do things like
 +// allocate memory - to create a spare m and put it on the list.
 +//
 +// Each of these extra m's also has a g0 and a curg that are
 +// pressed into service as the scheduling stack and current
 +// goroutine for the duration of the cgo callback.
 +//
 +// When the callback is done with the m, it calls dropm to
 +// put the m back on the list.
 +//go:nosplit
 +func needm(x byte) {
 +      if needextram != 0 {
 +              // Can happen if C/C++ code calls Go from a global ctor.
 +              // Can not throw, because scheduler is not initialized yet.
 +              // XXX
 +              // write(2, unsafe.Pointer("fatal error: cgo callback before cgo call\n"), sizeof("fatal error: cgo callback before cgo call\n") - 1)
 +              exit(1)
 +      }
 +
 +      // Lock extra list, take head, unlock popped list.
 +      // nilokay=false is safe here because of the invariant above,
 +      // that the extra list always contains or will soon contain
 +      // at least one m.
 +      mp := lockextra(false)
 +
 +      // Set needextram when we've just emptied the list,
 +      // so that the eventual call into cgocallbackg will
 +      // allocate a new m for the extra list. We delay the
 +      // allocation until then so that it can be done
 +      // after exitsyscall makes sure it is okay to be
 +      // running at all (that is, there's no garbage collection
 +      // running right now).
 +      mp.needextram = mp.schedlink == nil
 +      unlockextra(mp.schedlink)
 +
 +      // Install g (= m->g0) and set the stack bounds
 +      // to match the current stack. We don't actually know
 +      // how big the stack is, like we don't know how big any
 +      // scheduling stack is, but we assume there's at least 32 kB,
 +      // which is more than enough for us.
 +      setg(mp.g0)
 +      _g_ := getg()
 +      _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&x))) + 1024
 +      _g_.stack.lo = uintptr(noescape(unsafe.Pointer(&x))) - 32*1024
 +      _g_.stackguard0 = _g_.stack.lo + _StackGuard
 +
 +      // Initialize this thread to use the m.
 +      asminit()
 +      minit()
 +}
 +
 +// newextram allocates an m and puts it on the extra list.
 +// It is called with a working local m, so that it can do things
 +// like call schedlock and allocate.
 +func newextram() {
 +      // Create extra goroutine locked to extra m.
 +      // The goroutine is the context in which the cgo callback will run.
 +      // The sched.pc will never be returned to, but setting it to
 +      // goexit makes clear to the traceback routines where
 +      // the goroutine stack ends.
 +      mp := allocm(nil)
 +      gp := malg(4096)
 +      gp.sched.pc = funcPC(goexit) + _PCQuantum
 +      gp.sched.sp = gp.stack.hi
 +      gp.sched.sp -= 4 * regSize // extra space in case of reads slightly beyond frame
 +      gp.sched.lr = 0
 +      gp.sched.g = gp
 +      gp.syscallpc = gp.sched.pc
 +      gp.syscallsp = gp.sched.sp
 +      // malg returns status as Gidle, change to Gsyscall before adding to allg
 +      // where GC will see it.
 +      casgstatus(gp, _Gidle, _Gsyscall)
 +      gp.m = mp
 +      mp.curg = gp
 +      mp.locked = _LockInternal
 +      mp.lockedg = gp
 +      gp.lockedm = mp
 +      gp.goid = int64(xadd64(&sched.goidgen, 1))
 +      if raceenabled {
 +              gp.racectx = racegostart(funcPC(newextram))
 +      }
 +      // put on allg for garbage collector
 +      allgadd(gp)
 +
 +      // Add m to the extra list.
 +      mnext := lockextra(true)
 +      mp.schedlink = mnext
 +      unlockextra(mp)
 +}
 +
 +// dropm is called when a cgo callback has called needm but is now
 +// done with the callback and returning back into the non-Go thread.
 +// It puts the current m back onto the extra list.
 +//
 +// The main expense here is the call to signalstack to release the
 +// m's signal stack, and then the call to needm on the next callback
 +// from this thread. It is tempting to try to save the m for next time,
 +// which would eliminate both these costs, but there might not be
 +// a next time: the current thread (which Go does not control) might exit.
 +// If we saved the m for that thread, there would be an m leak each time
 +// such a thread exited. Instead, we acquire and release an m on each
 +// call. These should typically not be scheduling operations, just a few
 +// atomics, so the cost should be small.
 +//
 +// TODO(rsc): An alternative would be to allocate a dummy pthread per-thread
 +// variable using pthread_key_create. Unlike the pthread keys we already use
 +// on OS X, this dummy key would never be read by Go code. It would exist
 +// only so that we could register at thread-exit-time destructor.
 +// That destructor would put the m back onto the extra list.
 +// This is purely a performance optimization. The current version,
 +// in which dropm happens on each cgo call, is still correct too.
 +// We may have to keep the current version on systems with cgo
 +// but without pthreads, like Windows.
 +func dropm() {
 +      // Undo whatever initialization minit did during needm.
 +      unminit()
 +
 +      // Clear m and g, and return m to the extra list.
 +      // After the call to setmg we can only call nosplit functions.
 +      mp := getg().m
 +      setg(nil)
 +
 +      mnext := lockextra(true)
 +      mp.schedlink = mnext
 +      unlockextra(mp)
 +}
 +
 +var extram uintptr
 +
 +// lockextra locks the extra list and returns the list head.
 +// The caller must unlock the list by storing a new list head
 +// to extram. If nilokay is true, then lockextra will
 +// return a nil list head if that's what it finds. If nilokay is false,
 +// lockextra will keep waiting until the list head is no longer nil.
 +//go:nosplit
 +func lockextra(nilokay bool) *m {
 +      const locked = 1
 +
 +      for {
 +              old := atomicloaduintptr(&extram)
 +              if old == locked {
 +                      yield := osyield
 +                      yield()
 +                      continue
 +              }
 +              if old == 0 && !nilokay {
 +                      usleep(1)
 +                      continue
 +              }
 +              if casuintptr(&extram, old, locked) {
 +                      return (*m)(unsafe.Pointer(old))
 +              }
 +              yield := osyield
 +              yield()
 +              continue
 +      }
 +}
 +
 +//go:nosplit
 +func unlockextra(mp *m) {
 +      atomicstoreuintptr(&extram, uintptr(unsafe.Pointer(mp)))
 +}
 +
 +// Create a new m.  It will start off with a call to fn, or else the scheduler.
 +func _newm(fn func(), _p_ *p) {
 +      mp := allocm(_p_)
 +      mp.nextp = _p_
 +      mp.mstartfn = *(*unsafe.Pointer)(unsafe.Pointer(&fn))
 +
 +      if iscgo {
 +              var ts cgothreadstart
 +              if _cgo_thread_start == nil {
 +                      gothrow("_cgo_thread_start missing")
 +              }
 +              ts.g = mp.g0
 +              ts.tls = (*uint64)(unsafe.Pointer(&mp.tls[0]))
 +              ts.fn = unsafe.Pointer(funcPC(mstart))
 +              asmcgocall(_cgo_thread_start, unsafe.Pointer(&ts))
 +              return
 +      }
 +      newosproc(mp, unsafe.Pointer(mp.g0.stack.hi))
 +}
 +
 +// Stops execution of the current m until new work is available.
 +// Returns with acquired P.
 +func stopm() {
 +      _g_ := getg()
 +
 +      if _g_.m.locks != 0 {
 +              gothrow("stopm holding locks")
 +      }
 +      if _g_.m.p != nil {
 +              gothrow("stopm holding p")
 +      }
 +      if _g_.m.spinning {
 +              _g_.m.spinning = false
 +              xadd(&sched.nmspinning, -1)
 +      }
 +
 +retry:
 +      lock(&sched.lock)
 +      mput(_g_.m)
 +      unlock(&sched.lock)
 +      notesleep(&_g_.m.park)
 +      noteclear(&_g_.m.park)
 +      if _g_.m.helpgc != 0 {
 +              gchelper()
 +              _g_.m.helpgc = 0
 +              _g_.m.mcache = nil
 +              goto retry
 +      }
 +      acquirep(_g_.m.nextp)
 +      _g_.m.nextp = nil
 +}
 +
 +func mspinning() {
 +      getg().m.spinning = true
 +}
 +
 +// Schedules some M to run the p (creates an M if necessary).
 +// If p==nil, tries to get an idle P, if no idle P's does nothing.
 +func startm(_p_ *p, spinning bool) {
 +      lock(&sched.lock)
 +      if _p_ == nil {
 +              _p_ = pidleget()
 +              if _p_ == nil {
 +                      unlock(&sched.lock)
 +                      if spinning {
 +                              xadd(&sched.nmspinning, -1)
 +                      }
 +                      return
 +              }
 +      }
 +      mp := mget()
 +      unlock(&sched.lock)
 +      if mp == nil {
 +              var fn func()
 +              if spinning {
 +                      fn = mspinning
 +              }
 +              _newm(fn, _p_)
 +              return
 +      }
 +      if mp.spinning {
 +              gothrow("startm: m is spinning")
 +      }
 +      if mp.nextp != nil {
 +              gothrow("startm: m has p")
 +      }
 +      mp.spinning = spinning
 +      mp.nextp = _p_
 +      notewakeup(&mp.park)
 +}
 +
 +// Hands off P from syscall or locked M.
 +func handoffp(_p_ *p) {
 +      // if it has local work, start it straight away
 +      if _p_.runqhead != _p_.runqtail || sched.runqsize != 0 {
 +              startm(_p_, false)
 +              return
 +      }
 +      // no local work, check that there are no spinning/idle M's,
 +      // otherwise our help is not required
 +      if atomicload(&sched.nmspinning)+atomicload(&sched.npidle) == 0 && cas(&sched.nmspinning, 0, 1) { // TODO: fast atomic
 +              startm(_p_, true)
 +              return
 +      }
 +      lock(&sched.lock)
 +      if sched.gcwaiting != 0 {
 +              _p_.status = _Pgcstop
 +              sched.stopwait--
 +              if sched.stopwait == 0 {
 +                      notewakeup(&sched.stopnote)
 +              }
 +              unlock(&sched.lock)
 +              return
 +      }
 +      if sched.runqsize != 0 {
 +              unlock(&sched.lock)
 +              startm(_p_, false)
 +              return
 +      }
 +      // If this is the last running P and nobody is polling network,
 +      // need to wakeup another M to poll network.
 +      if sched.npidle == uint32(gomaxprocs-1) && atomicload64(&sched.lastpoll) != 0 {
 +              unlock(&sched.lock)
 +              startm(_p_, false)
 +              return
 +      }
 +      pidleput(_p_)
 +      unlock(&sched.lock)
 +}
 +
 +// Tries to add one more P to execute G's.
 +// Called when a G is made runnable (newproc, ready).
 +func wakep() {
 +      // be conservative about spinning threads
 +      if !cas(&sched.nmspinning, 0, 1) {
 +              return
 +      }
 +      startm(nil, true)
 +}
 +
 +// Stops execution of the current m that is locked to a g until the g is runnable again.
 +// Returns with acquired P.
 +func stoplockedm() {
 +      _g_ := getg()
 +
 +      if _g_.m.lockedg == nil || _g_.m.lockedg.lockedm != _g_.m {
 +              gothrow("stoplockedm: inconsistent locking")
 +      }
 +      if _g_.m.p != nil {
 +              // Schedule another M to run this p.
 +              _p_ := releasep()
 +              handoffp(_p_)
 +      }
 +      incidlelocked(1)
 +      // Wait until another thread schedules lockedg again.
 +      notesleep(&_g_.m.park)
 +      noteclear(&_g_.m.park)
 +      status := readgstatus(_g_.m.lockedg)
 +      if status&^_Gscan != _Grunnable {
 +              print("runtime:stoplockedm: g is not Grunnable or Gscanrunnable\n")
 +              dumpgstatus(_g_)
 +              gothrow("stoplockedm: not runnable")
 +      }
 +      acquirep(_g_.m.nextp)
 +      _g_.m.nextp = nil
 +}
 +
 +// Schedules the locked m to run the locked gp.
 +func startlockedm(gp *g) {
 +      _g_ := getg()
 +
 +      mp := gp.lockedm
 +      if mp == _g_.m {
 +              gothrow("startlockedm: locked to me")
 +      }
 +      if mp.nextp != nil {
 +              gothrow("startlockedm: m has p")
 +      }
 +      // directly handoff current P to the locked m
 +      incidlelocked(-1)
 +      _p_ := releasep()
 +      mp.nextp = _p_
 +      notewakeup(&mp.park)
 +      stopm()
 +}
 +
 +// Stops the current m for stoptheworld.
 +// Returns when the world is restarted.
 +func gcstopm() {
 +      _g_ := getg()
 +
 +      if sched.gcwaiting == 0 {
 +              gothrow("gcstopm: not waiting for gc")
 +      }
 +      if _g_.m.spinning {
 +              _g_.m.spinning = false
 +              xadd(&sched.nmspinning, -1)
 +      }
 +      _p_ := releasep()
 +      lock(&sched.lock)
 +      _p_.status = _Pgcstop
 +      sched.stopwait--
 +      if sched.stopwait == 0 {
 +              notewakeup(&sched.stopnote)
 +      }
 +      unlock(&sched.lock)
 +      stopm()
 +}
 +
 +// Schedules gp to run on the current M.
 +// Never returns.
 +func execute(gp *g) {
 +      _g_ := getg()
 +
 +      casgstatus(gp, _Grunnable, _Grunning)
 +      gp.waitsince = 0
 +      gp.preempt = false
 +      gp.stackguard0 = gp.stack.lo + _StackGuard
 +      _g_.m.p.schedtick++
 +      _g_.m.curg = gp
 +      gp.m = _g_.m
 +
 +      // Check whether the profiler needs to be turned on or off.
 +      hz := sched.profilehz
 +      if _g_.m.profilehz != hz {
 +              resetcpuprofiler(hz)
 +      }
 +
 +      gogo(&gp.sched)
 +}
 +
 +// Finds a runnable goroutine to execute.
 +// Tries to steal from other P's, get g from global queue, poll network.
 +func findrunnable() *g {
 +      _g_ := getg()
 +
 +top:
 +      if sched.gcwaiting != 0 {
 +              gcstopm()
 +              goto top
 +      }
 +      if fingwait && fingwake {
 +              if gp := wakefing(); gp != nil {
 +                      ready(gp)
 +              }
 +      }
 +
 +      // local runq
 +      if gp := runqget(_g_.m.p); gp != nil {
 +              return gp
 +      }
 +
 +      // global runq
 +      if sched.runqsize != 0 {
 +              lock(&sched.lock)
 +              gp := globrunqget(_g_.m.p, 0)
 +              unlock(&sched.lock)
 +              if gp != nil {
 +                      return gp
 +              }
 +      }
 +
 +      // poll network - returns list of goroutines
 +      if gp := netpoll(false); gp != nil { // non-blocking
 +              injectglist(gp.schedlink)
 +              casgstatus(gp, _Gwaiting, _Grunnable)
 +              return gp
 +      }
 +
 +      // If number of spinning M's >= number of busy P's, block.
 +      // This is necessary to prevent excessive CPU consumption
 +      // when GOMAXPROCS>>1 but the program parallelism is low.
 +      if !_g_.m.spinning && 2*atomicload(&sched.nmspinning) >= uint32(gomaxprocs)-atomicload(&sched.npidle) { // TODO: fast atomic
 +              goto stop
 +      }
 +      if !_g_.m.spinning {
 +              _g_.m.spinning = true
 +              xadd(&sched.nmspinning, 1)
 +      }
 +      // random steal from other P's
 +      for i := 0; i < int(2*gomaxprocs); i++ {
 +              if sched.gcwaiting != 0 {
 +                      goto top
 +              }
 +              _p_ := allp[fastrand1()%uint32(gomaxprocs)]
 +              var gp *g
 +              if _p_ == _g_.m.p {
 +                      gp = runqget(_p_)
 +              } else {
 +                      gp = runqsteal(_g_.m.p, _p_)
 +              }
 +              if gp != nil {
 +                      return gp
 +              }
 +      }
 +stop:
 +
 +      // return P and block
 +      lock(&sched.lock)
 +      if sched.gcwaiting != 0 {
 +              unlock(&sched.lock)
 +              goto top
 +      }
 +      if sched.runqsize != 0 {
 +              gp := globrunqget(_g_.m.p, 0)
 +              unlock(&sched.lock)
 +              return gp
 +      }
 +      _p_ := releasep()
 +      pidleput(_p_)
 +      unlock(&sched.lock)
 +      if _g_.m.spinning {
 +              _g_.m.spinning = false
 +              xadd(&sched.nmspinning, -1)
 +      }
 +
 +      // check all runqueues once again
 +      for i := 0; i < int(gomaxprocs); i++ {
 +              _p_ := allp[i]
 +              if _p_ != nil && _p_.runqhead != _p_.runqtail {
 +                      lock(&sched.lock)
 +                      _p_ = pidleget()
 +                      unlock(&sched.lock)
 +                      if _p_ != nil {
 +                              acquirep(_p_)
 +                              goto top
 +                      }
 +                      break
 +              }
 +      }
 +
 +      // poll network
 +      if xchg64(&sched.lastpoll, 0) != 0 {
 +              if _g_.m.p != nil {
 +                      gothrow("findrunnable: netpoll with p")
 +              }
 +              if _g_.m.spinning {
 +                      gothrow("findrunnable: netpoll with spinning")
 +              }
 +              gp := netpoll(true) // block until new work is available
 +              atomicstore64(&sched.lastpoll, uint64(nanotime()))
 +              if gp != nil {
 +                      lock(&sched.lock)
 +                      _p_ = pidleget()
 +                      unlock(&sched.lock)
 +                      if _p_ != nil {
 +                              acquirep(_p_)
 +                              injectglist(gp.schedlink)
 +                              casgstatus(gp, _Gwaiting, _Grunnable)
 +                              return gp
 +                      }
 +                      injectglist(gp)
 +              }
 +      }
 +      stopm()
 +      goto top
 +}
 +
 +func resetspinning() {
 +      _g_ := getg()
 +
 +      var nmspinning uint32
 +      if _g_.m.spinning {
 +              _g_.m.spinning = false
 +              nmspinning = xadd(&sched.nmspinning, -1)
 +              if nmspinning < 0 {
 +                      gothrow("findrunnable: negative nmspinning")
 +              }
 +      } else {
 +              nmspinning = atomicload(&sched.nmspinning)
 +      }
 +
 +      // M wakeup policy is deliberately somewhat conservative (see nmspinning handling),
 +      // so see if we need to wakeup another P here.
 +      if nmspinning == 0 && atomicload(&sched.npidle) > 0 {
 +              wakep()
 +      }
 +}
 +
 +// Injects the list of runnable G's into the scheduler.
 +// Can run concurrently with GC.
 +func injectglist(glist *g) {
 +      if glist == nil {
 +              return
 +      }
 +      lock(&sched.lock)
 +      var n int
 +      for n = 0; glist != nil; n++ {
 +              gp := glist
 +              glist = gp.schedlink
 +              casgstatus(gp, _Gwaiting, _Grunnable)
 +              globrunqput(gp)
 +      }
 +      unlock(&sched.lock)
 +      for ; n != 0 && sched.npidle != 0; n-- {
 +              startm(nil, false)
 +      }
 +}
 +
 +// One round of scheduler: find a runnable goroutine and execute it.
 +// Never returns.
 +func schedule() {
 +      _g_ := getg()
 +
 +      if _g_.m.locks != 0 {
 +              gothrow("schedule: holding locks")
 +      }
 +
 +      if _g_.m.lockedg != nil {
 +              stoplockedm()
 +              execute(_g_.m.lockedg) // Never returns.
 +      }
 +
 +top:
 +      if sched.gcwaiting != 0 {
 +              gcstopm()
 +              goto top
 +      }
 +
 +      var gp *g
 +      // Check the global runnable queue once in a while to ensure fairness.
 +      // Otherwise two goroutines can completely occupy the local runqueue
 +      // by constantly respawning each other.
 +      tick := _g_.m.p.schedtick
 +      // This is a fancy way to say tick%61==0,
 +      // it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors.
 +      if uint64(tick)-((uint64(tick)*0x4325c53f)>>36)*61 == 0 && sched.runqsize > 0 {
 +              lock(&sched.lock)
 +              gp = globrunqget(_g_.m.p, 1)
 +              unlock(&sched.lock)
 +              if gp != nil {
 +                      resetspinning()
 +              }
 +      }
 +      if gp == nil {
 +              gp = runqget(_g_.m.p)
 +              if gp != nil && _g_.m.spinning {
 +                      gothrow("schedule: spinning with local work")
 +              }
 +      }
 +      if gp == nil {
 +              gp = findrunnable() // blocks until work is available
 +              resetspinning()
 +      }
 +
 +      if gp.lockedm != nil {
 +              // Hands off own p to the locked m,
 +              // then blocks waiting for a new p.
 +              startlockedm(gp)
 +              goto top
 +      }
 +
 +      execute(gp)
 +}
 +
 +// dropg removes the association between m and the current goroutine m->curg (gp for short).
 +// Typically a caller sets gp's status away from Grunning and then
 +// immediately calls dropg to finish the job. The caller is also responsible
 +// for arranging that gp will be restarted using ready at an
 +// appropriate time. After calling dropg and arranging for gp to be
 +// readied later, the caller can do other work but eventually should
 +// call schedule to restart the scheduling of goroutines on this m.
 +func dropg() {
 +      _g_ := getg()
 +
 +      if _g_.m.lockedg == nil {
 +              _g_.m.curg.m = nil
 +              _g_.m.curg = nil
 +      }
 +}
 +
 +// Puts the current goroutine into a waiting state and calls unlockf.
 +// If unlockf returns false, the goroutine is resumed.
 +func park(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string) {
 +      _g_ := getg()
 +
 +      _g_.m.waitlock = lock
 +      _g_.m.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf))
 +      _g_.waitreason = reason
 +      mcall(park_m)
 +}
 +
 +func parkunlock_c(gp *g, lock unsafe.Pointer) bool {
 +      unlock((*mutex)(lock))
 +      return true
 +}
 +
 +// Puts the current goroutine into a waiting state and unlocks the lock.
 +// The goroutine can be made runnable again by calling ready(gp).
 +func parkunlock(lock *mutex, reason string) {
 +      park(parkunlock_c, unsafe.Pointer(lock), reason)
 +}
 +
 +// park continuation on g0.
 +func park_m(gp *g) {
 +      _g_ := getg()
 +
 +      casgstatus(gp, _Grunning, _Gwaiting)
 +      dropg()
 +
 +      if _g_.m.waitunlockf != nil {
 +              fn := *(*func(*g, unsafe.Pointer) bool)(unsafe.Pointer(&_g_.m.waitunlockf))
 +              ok := fn(gp, _g_.m.waitlock)
 +              _g_.m.waitunlockf = nil
 +              _g_.m.waitlock = nil
 +              if !ok {
 +                      casgstatus(gp, _Gwaiting, _Grunnable)
 +                      execute(gp) // Schedule it back, never returns.
 +              }
 +      }
 +      schedule()
 +}
 +
 +// Gosched continuation on g0.
 +func gosched_m(gp *g) {
 +      status := readgstatus(gp)
 +      if status&^_Gscan != _Grunning {
 +              dumpgstatus(gp)
 +              gothrow("bad g status")
 +      }
 +      casgstatus(gp, _Grunning, _Grunnable)
 +      dropg()
 +      lock(&sched.lock)
 +      globrunqput(gp)
 +      unlock(&sched.lock)
 +
 +      schedule()
 +}
 +
 +// Finishes execution of the current goroutine.
 +// Must be NOSPLIT because it is called from Go. (TODO - probably not anymore)
 +//go:nosplit
 +func goexit1() {
 +      if raceenabled {
 +              racegoend()
 +      }
 +      mcall(goexit0)
 +}
 +
 +// goexit continuation on g0.
 +func goexit0(gp *g) {
 +      _g_ := getg()
 +
 +      casgstatus(gp, _Grunning, _Gdead)
 +      gp.m = nil
 +      gp.lockedm = nil
 +      _g_.m.lockedg = nil
 +      gp.paniconfault = false
 +      gp._defer = nil // should be true already but just in case.
 +      gp._panic = nil // non-nil for Goexit during panic. points at stack-allocated data.
 +      gp.writebuf = nil
 +      gp.waitreason = ""
 +      gp.param = nil
 +
 +      dropg()
 +
 +      if _g_.m.locked&^_LockExternal != 0 {
 +              print("invalid m->locked = ", _g_.m.locked, "\n")
 +              gothrow("internal lockOSThread error")
 +      }
 +      _g_.m.locked = 0
 +      gfput(_g_.m.p, gp)
 +      schedule()
 +}
 +
 +//go:nosplit
 +func save(pc, sp uintptr) {
 +      _g_ := getg()
 +
 +      _g_.sched.pc = pc
 +      _g_.sched.sp = sp
 +      _g_.sched.lr = 0
 +      _g_.sched.ret = 0
 +      _g_.sched.ctxt = nil
 +      _g_.sched.g = _g_
 +}
 +
 +// The goroutine g is about to enter a system call.
 +// Record that it's not using the cpu anymore.
 +// This is called only from the go syscall library and cgocall,
 +// not from the low-level system calls used by the
 +//
 +// Entersyscall cannot split the stack: the gosave must
 +// make g->sched refer to the caller's stack segment, because
 +// entersyscall is going to return immediately after.
 +//
 +// Nothing entersyscall calls can split the stack either.
 +// We cannot safely move the stack during an active call to syscall,
 +// because we do not know which of the uintptr arguments are
 +// really pointers (back into the stack).
 +// In practice, this means that we make the fast path run through
 +// entersyscall doing no-split things, and the slow path has to use systemstack
 +// to run bigger things on the system stack.
 +//
 +// reentersyscall is the entry point used by cgo callbacks, where explicitly
 +// saved SP and PC are restored. This is needed when exitsyscall will be called
 +// from a function further up in the call stack than the parent, as g->syscallsp
 +// must always point to a valid stack frame. entersyscall below is the normal
 +// entry point for syscalls, which obtains the SP and PC from the caller.
 +//go:nosplit
 +func reentersyscall(pc, sp uintptr) {
 +      _g_ := getg()
 +
 +      // Disable preemption because during this function g is in Gsyscall status,
 +      // but can have inconsistent g->sched, do not let GC observe it.
 +      _g_.m.locks++
 +
 +      // Entersyscall must not call any function that might split/grow the stack.
 +      // (See details in comment above.)
 +      // Catch calls that might, by replacing the stack guard with something that
 +      // will trip any stack check and leaving a flag to tell newstack to die.
 +      _g_.stackguard0 = stackPreempt
 +      _g_.throwsplit = true
 +
 +      // Leave SP around for GC and traceback.
 +      save(pc, sp)
 +      _g_.syscallsp = sp
 +      _g_.syscallpc = pc
 +      casgstatus(_g_, _Grunning, _Gsyscall)
 +      if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp {
 +              systemstack(entersyscall_bad)
 +      }
 +
 +      if atomicload(&sched.sysmonwait) != 0 { // TODO: fast atomic
 +              systemstack(entersyscall_sysmon)
 +              save(pc, sp)
 +      }
 +
 +      _g_.m.mcache = nil
 +      _g_.m.p.m = nil
 +      atomicstore(&_g_.m.p.status, _Psyscall)
 +      if sched.gcwaiting != 0 {
 +              systemstack(entersyscall_gcwait)
 +              save(pc, sp)
 +      }
 +
 +      // Goroutines must not split stacks in Gsyscall status (it would corrupt g->sched).
 +      // We set _StackGuard to StackPreempt so that first split stack check calls morestack.
 +      // Morestack detects this case and throws.
 +      _g_.stackguard0 = stackPreempt
 +      _g_.m.locks--
 +}
 +
 +// Standard syscall entry used by the go syscall library and normal cgo calls.
 +//go:nosplit
 +func entersyscall(dummy int32) {
 +      reentersyscall(getcallerpc(unsafe.Pointer(&dummy)), getcallersp(unsafe.Pointer(&dummy)))
 +}
 +
 +func entersyscall_bad() {
 +      var gp *g
 +      gp = getg().m.curg
 +      print("entersyscall inconsistent ", hex(gp.syscallsp), " [", hex(gp.stack.lo), ",", hex(gp.stack.hi), "]\n")
 +      gothrow("entersyscall")
 +}
 +
 +func entersyscall_sysmon() {
 +      lock(&sched.lock)
 +      if atomicload(&sched.sysmonwait) != 0 {
 +              atomicstore(&sched.sysmonwait, 0)
 +              notewakeup(&sched.sysmonnote)
 +      }
 +      unlock(&sched.lock)
 +}
 +
 +func entersyscall_gcwait() {
 +      _g_ := getg()
 +
 +      lock(&sched.lock)
 +      if sched.stopwait > 0 && cas(&_g_.m.p.status, _Psyscall, _Pgcstop) {
 +              if sched.stopwait--; sched.stopwait == 0 {
 +                      notewakeup(&sched.stopnote)
 +              }
 +      }
 +      unlock(&sched.lock)
 +}
 +
 +// The same as entersyscall(), but with a hint that the syscall is blocking.
 +//go:nosplit
 +func entersyscallblock(dummy int32) {
 +      _g_ := getg()
 +
 +      _g_.m.locks++ // see comment in entersyscall
 +      _g_.throwsplit = true
 +      _g_.stackguard0 = stackPreempt // see comment in entersyscall
 +
 +      // Leave SP around for GC and traceback.
 +      save(getcallerpc(unsafe.Pointer(&dummy)), getcallersp(unsafe.Pointer(&dummy)))
 +      _g_.syscallsp = _g_.sched.sp
 +      _g_.syscallpc = _g_.sched.pc
 +      casgstatus(_g_, _Grunning, _Gsyscall)
 +      if _g_.syscallsp < _g_.stack.lo || _g_.stack.hi < _g_.syscallsp {
 +              systemstack(entersyscall_bad)
 +      }
 +
 +      systemstack(entersyscallblock_handoff)
 +
 +      // Resave for traceback during blocked call.
 +      save(getcallerpc(unsafe.Pointer(&dummy)), getcallersp(unsafe.Pointer(&dummy)))
 +
 +      _g_.m.locks--
 +}
 +
 +func entersyscallblock_handoff() {
 +      handoffp(releasep())
 +}
 +
 +// The goroutine g exited its system call.
 +// Arrange for it to run on a cpu again.
 +// This is called only from the go syscall library, not
 +// from the low-level system calls used by the
 +//go:nosplit
 +func exitsyscall(dummy int32) {
 +      _g_ := getg()
 +
 +      _g_.m.locks++ // see comment in entersyscall
 +      if getcallersp(unsafe.Pointer(&dummy)) > _g_.syscallsp {
 +              gothrow("exitsyscall: syscall frame is no longer valid")
 +      }
 +
 +      _g_.waitsince = 0
 +      if exitsyscallfast() {
 +              if _g_.m.mcache == nil {
 +                      gothrow("lost mcache")
 +              }
 +              // There's a cpu for us, so we can run.
 +              _g_.m.p.syscalltick++
 +              // We need to cas the status and scan before resuming...
 +              casgstatus(_g_, _Gsyscall, _Grunning)
 +
 +              // Garbage collector isn't running (since we are),
 +              // so okay to clear syscallsp.
 +              _g_.syscallsp = 0
 +              _g_.m.locks--
 +              if _g_.preempt {
 +                      // restore the preemption request in case we've cleared it in newstack
 +                      _g_.stackguard0 = stackPreempt
 +              } else {
 +                      // otherwise restore the real _StackGuard, we've spoiled it in entersyscall/entersyscallblock
 +                      _g_.stackguard0 = _g_.stack.lo + _StackGuard
 +              }
 +              _g_.throwsplit = false
 +              return
 +      }
 +
 +      _g_.m.locks--
 +
 +      // Call the scheduler.
 +      mcall(exitsyscall0)
 +
 +      if _g_.m.mcache == nil {
 +              gothrow("lost mcache")
 +      }
 +
 +      // Scheduler returned, so we're allowed to run now.
 +      // Delete the syscallsp information that we left for
 +      // the garbage collector during the system call.
 +      // Must wait until now because until gosched returns
 +      // we don't know for sure that the garbage collector
 +      // is not running.
 +      _g_.syscallsp = 0
 +      _g_.m.p.syscalltick++
 +      _g_.throwsplit = false
 +}
 +
 +//go:nosplit
 +func exitsyscallfast() bool {
 +      _g_ := getg()
 +
 +      // Freezetheworld sets stopwait but does not retake P's.
 +      if sched.stopwait != 0 {
 +              _g_.m.p = nil
 +              return false
 +      }
 +
 +      // Try to re-acquire the last P.
 +      if _g_.m.p != nil && _g_.m.p.status == _Psyscall && cas(&_g_.m.p.status, _Psyscall, _Prunning) {
 +              // There's a cpu for us, so we can run.
 +              _g_.m.mcache = _g_.m.p.mcache
 +              _g_.m.p.m = _g_.m
 +              return true
 +      }
 +
 +      // Try to get any other idle P.
 +      _g_.m.p = nil
 +      if sched.pidle != nil {
 +              var ok bool
 +              systemstack(func() {
 +                      ok = exitsyscallfast_pidle()
 +              })
 +              if ok {
 +                      return true
 +              }
 +      }
 +      return false
 +}
 +
 +func exitsyscallfast_pidle() bool {
 +      lock(&sched.lock)
 +      _p_ := pidleget()
 +      if _p_ != nil && atomicload(&sched.sysmonwait) != 0 {
 +              atomicstore(&sched.sysmonwait, 0)
 +              notewakeup(&sched.sysmonnote)
 +      }
 +      unlock(&sched.lock)
 +      if _p_ != nil {
 +              acquirep(_p_)
 +              return true
 +      }
 +      return false
 +}
 +
 +// exitsyscall slow path on g0.
 +// Failed to acquire P, enqueue gp as runnable.
 +func exitsyscall0(gp *g) {
 +      _g_ := getg()
 +
 +      casgstatus(gp, _Gsyscall, _Grunnable)
 +      dropg()
 +      lock(&sched.lock)
 +      _p_ := pidleget()
 +      if _p_ == nil {
 +              globrunqput(gp)
 +      } else if atomicload(&sched.sysmonwait) != 0 {
 +              atomicstore(&sched.sysmonwait, 0)
 +              notewakeup(&sched.sysmonnote)
 +      }
 +      unlock(&sched.lock)
 +      if _p_ != nil {
 +              acquirep(_p_)
 +              execute(gp) // Never returns.
 +      }
 +      if _g_.m.lockedg != nil {
 +              // Wait until another thread schedules gp and so m again.
 +              stoplockedm()
 +              execute(gp) // Never returns.
 +      }
 +      stopm()
 +      schedule() // Never returns.
 +}
 +
 +func beforefork() {
 +      gp := getg().m.curg
 +
 +      // Fork can hang if preempted with signals frequently enough (see issue 5517).
 +      // Ensure that we stay on the same M where we disable profiling.
 +      gp.m.locks++
 +      if gp.m.profilehz != 0 {
 +              resetcpuprofiler(0)
 +      }
 +
 +      // This function is called before fork in syscall package.
 +      // Code between fork and exec must not allocate memory nor even try to grow stack.
 +      // Here we spoil g->_StackGuard to reliably detect any attempts to grow stack.
 +      // runtime_AfterFork will undo this in parent process, but not in child.
 +      gp.stackguard0 = stackFork
 +}
 +
 +// Called from syscall package before fork.
 +//go:nosplit
 +func syscall_BeforeFork() {
 +      systemstack(beforefork)
 +}
 +
 +func afterfork() {
 +      gp := getg().m.curg
 +
 +      // See the comment in beforefork.
 +      gp.stackguard0 = gp.stack.lo + _StackGuard
 +
 +      hz := sched.profilehz
 +      if hz != 0 {
 +              resetcpuprofiler(hz)
 +      }
 +      gp.m.locks--
 +}
 +
 +// Called from syscall package after fork in parent.
 +//go:nosplit
 +func syscall_AfterFork() {
 +      systemstack(afterfork)
 +}
 +
 +// Allocate a new g, with a stack big enough for stacksize bytes.
 +func malg(stacksize int32) *g {
 +      newg := allocg()
 +      if stacksize >= 0 {
 +              stacksize = round2(_StackSystem + stacksize)
 +              systemstack(func() {
 +                      newg.stack = stackalloc(uint32(stacksize))
 +              })
 +              newg.stackguard0 = newg.stack.lo + _StackGuard
 +              newg.stackguard1 = ^uintptr(0)
 +      }
 +      return newg
 +}
 +
 +// Create a new g running fn with siz bytes of arguments.
 +// Put it on the queue of g's waiting to run.
 +// The compiler turns a go statement into a call to this.
 +// Cannot split the stack because it assumes that the arguments
 +// are available sequentially after &fn; they would not be
 +// copied if a stack split occurred.
 +//go:nosplit
 +func newproc(siz int32, fn *funcval) {
 +      argp := add(unsafe.Pointer(&fn), ptrSize)
 +      if hasLinkRegister {
 +              argp = add(argp, ptrSize) // skip caller's saved LR
 +      }
 +
 +      pc := getcallerpc(unsafe.Pointer(&siz))
 +      systemstack(func() {
 +              newproc1(fn, (*uint8)(argp), siz, 0, pc)
 +      })
 +}
 +
 +// Create a new g running fn with narg bytes of arguments starting
 +// at argp and returning nret bytes of results.  callerpc is the
 +// address of the go statement that created this.  The new g is put
 +// on the queue of g's waiting to run.
 +func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr) *g {
 +      _g_ := getg()
 +
 +      if fn == nil {
 +              _g_.m.throwing = -1 // do not dump full stacks
 +              gothrow("go of nil func value")
 +      }
 +      _g_.m.locks++ // disable preemption because it can be holding p in a local var
 +      siz := narg + nret
 +      siz = (siz + 7) &^ 7
 +
 +      // We could allocate a larger initial stack if necessary.
 +      // Not worth it: this is almost always an error.
 +      // 4*sizeof(uintreg): extra space added below
 +      // sizeof(uintreg): caller's LR (arm) or return address (x86, in gostartcall).
 +      if siz >= _StackMin-4*regSize-regSize {
 +              gothrow("newproc: function arguments too large for new goroutine")
 +      }
 +
 +      _p_ := _g_.m.p
 +      newg := gfget(_p_)
 +      if newg == nil {
 +              newg = malg(_StackMin)
 +              casgstatus(newg, _Gidle, _Gdead)
 +              allgadd(newg) // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
 +      }
 +      if newg.stack.hi == 0 {
 +              gothrow("newproc1: newg missing stack")
 +      }
 +
 +      if readgstatus(newg) != _Gdead {
 +              gothrow("newproc1: new g is not Gdead")
 +      }
 +
 +      sp := newg.stack.hi
 +      sp -= 4 * regSize // extra space in case of reads slightly beyond frame
 +      sp -= uintptr(siz)
 +      memmove(unsafe.Pointer(sp), unsafe.Pointer(argp), uintptr(narg))
 +      if hasLinkRegister {
 +              // caller's LR
 +              sp -= ptrSize
 +              *(*unsafe.Pointer)(unsafe.Pointer(sp)) = nil
 +      }
 +
 +      memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
 +      newg.sched.sp = sp
 +      newg.sched.pc = funcPC(goexit) + _PCQuantum // +PCQuantum so that previous instruction is in same function
 +      newg.sched.g = newg
 +      gostartcallfn(&newg.sched, fn)
 +      newg.gopc = callerpc
 +      casgstatus(newg, _Gdead, _Grunnable)
 +
 +      if _p_.goidcache == _p_.goidcacheend {
 +              // Sched.goidgen is the last allocated id,
 +              // this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].
 +              // At startup sched.goidgen=0, so main goroutine receives goid=1.
 +              _p_.goidcache = xadd64(&sched.goidgen, _GoidCacheBatch)
 +              _p_.goidcache -= _GoidCacheBatch - 1
 +              _p_.goidcacheend = _p_.goidcache + _GoidCacheBatch
 +      }
 +      newg.goid = int64(_p_.goidcache)
 +      _p_.goidcache++
 +      if raceenabled {
 +              newg.racectx = racegostart(callerpc)
 +      }
 +      runqput(_p_, newg)
 +
 +      if atomicload(&sched.npidle) != 0 && atomicload(&sched.nmspinning) == 0 && unsafe.Pointer(fn.fn) != unsafe.Pointer(funcPC(main)) { // TODO: fast atomic
 +              wakep()
 +      }
 +      _g_.m.locks--
 +      if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack
 +              _g_.stackguard0 = stackPreempt
 +      }
 +      return newg
 +}
 +
 +// Put on gfree list.
 +// If local list is too long, transfer a batch to the global list.
 +func gfput(_p_ *p, gp *g) {
 +      if readgstatus(gp) != _Gdead {
 +              gothrow("gfput: bad status (not Gdead)")
 +      }
 +
 +      stksize := gp.stack.hi - gp.stack.lo
 +
 +      if stksize != _FixedStack {
 +              // non-standard stack size - free it.
 +              stackfree(gp.stack)
 +              gp.stack.lo = 0
 +              gp.stack.hi = 0
 +              gp.stackguard0 = 0
 +      }
 +
 +      gp.schedlink = _p_.gfree
 +      _p_.gfree = gp
 +      _p_.gfreecnt++
 +      if _p_.gfreecnt >= 64 {
 +              lock(&sched.gflock)
 +              for _p_.gfreecnt >= 32 {
 +                      _p_.gfreecnt--
 +                      gp = _p_.gfree
 +                      _p_.gfree = gp.schedlink
 +                      gp.schedlink = sched.gfree
 +                      sched.gfree = gp
 +                      sched.ngfree++
 +              }
 +              unlock(&sched.gflock)
 +      }
 +}
 +
 +// Get from gfree list.
 +// If local list is empty, grab a batch from global list.
 +func gfget(_p_ *p) *g {
 +retry:
 +      gp := _p_.gfree
 +      if gp == nil && sched.gfree != nil {
 +              lock(&sched.gflock)
 +              for _p_.gfreecnt < 32 && sched.gfree != nil {
 +                      _p_.gfreecnt++
 +                      gp = sched.gfree
 +                      sched.gfree = gp.schedlink
 +                      sched.ngfree--
 +                      gp.schedlink = _p_.gfree
 +                      _p_.gfree = gp
 +              }
 +              unlock(&sched.gflock)
 +              goto retry
 +      }
 +      if gp != nil {
 +              _p_.gfree = gp.schedlink
 +              _p_.gfreecnt--
 +              if gp.stack.lo == 0 {
 +                      // Stack was deallocated in gfput.  Allocate a new one.
 +                      systemstack(func() {
 +                              gp.stack = stackalloc(_FixedStack)
 +                      })
 +                      gp.stackguard0 = gp.stack.lo + _StackGuard
 +              } else {
 +                      if raceenabled {
 +                              racemalloc(unsafe.Pointer(gp.stack.lo), gp.stack.hi-gp.stack.lo)
 +                      }
 +              }
 +      }
 +      return gp
 +}
 +
 +// Purge all cached G's from gfree list to the global list.
 +func gfpurge(_p_ *p) {
 +      lock(&sched.gflock)
 +      for _p_.gfreecnt != 0 {
 +              _p_.gfreecnt--
 +              gp := _p_.gfree
 +              _p_.gfree = gp.schedlink
 +              gp.schedlink = sched.gfree
 +              sched.gfree = gp
 +              sched.ngfree++
 +      }
 +      unlock(&sched.gflock)
 +}
 +
 +// Breakpoint executes a breakpoint trap.
 +func Breakpoint() {
 +      breakpoint()
 +}
 +
 +// dolockOSThread is called by LockOSThread and lockOSThread below
 +// after they modify m.locked. Do not allow preemption during this call,
 +// or else the m might be different in this function than in the caller.
 +//go:nosplit
 +func dolockOSThread() {
 +      _g_ := getg()
 +      _g_.m.lockedg = _g_
 +      _g_.lockedm = _g_.m
 +}
 +
 +//go:nosplit
 +
 +// LockOSThread wires the calling goroutine to its current operating system thread.
 +// Until the calling goroutine exits or calls UnlockOSThread, it will always
 +// execute in that thread, and no other goroutine can.
 +func LockOSThread() {
 +      getg().m.locked |= _LockExternal
 +      dolockOSThread()
 +}
 +
 +//go:nosplit
 +func lockOSThread() {
 +      getg().m.locked += _LockInternal
 +      dolockOSThread()
 +}
 +
 +// dounlockOSThread is called by UnlockOSThread and unlockOSThread below
 +// after they update m->locked. Do not allow preemption during this call,
 +// or else the m might be in different in this function than in the caller.
 +//go:nosplit
 +func dounlockOSThread() {
 +      _g_ := getg()
 +      if _g_.m.locked != 0 {
 +              return
 +      }
 +      _g_.m.lockedg = nil
 +      _g_.lockedm = nil
 +}
 +
 +//go:nosplit
 +
 +// UnlockOSThread unwires the calling goroutine from its fixed operating system thread.
 +// If the calling goroutine has not called LockOSThread, UnlockOSThread is a no-op.
 +func UnlockOSThread() {
 +      getg().m.locked &^= _LockExternal
 +      dounlockOSThread()
 +}
 +
 +//go:nosplit
 +func unlockOSThread() {
 +      _g_ := getg()
 +      if _g_.m.locked < _LockInternal {
 +              systemstack(badunlockosthread)
 +      }
 +      _g_.m.locked -= _LockInternal
 +      dounlockOSThread()
 +}
 +
 +func badunlockosthread() {
 +      gothrow("runtime: internal error: misuse of lockOSThread/unlockOSThread")
 +}
 +
 +func gcount() int32 {
 +      n := int32(allglen) - sched.ngfree
 +      for i := 0; ; i++ {
 +              _p_ := allp[i]
 +              if _p_ == nil {
 +                      break
 +              }
 +              n -= _p_.gfreecnt
 +      }
 +
 +      // All these variables can be changed concurrently, so the result can be inconsistent.
 +      // But at least the current goroutine is running.
 +      if n < 1 {
 +              n = 1
 +      }
 +      return n
 +}
 +
 +func mcount() int32 {
 +      return sched.mcount
 +}
 +
 +var prof struct {
 +      lock uint32
 +      hz   int32
 +}
 +
 +func _System()       { _System() }
 +func _ExternalCode() { _ExternalCode() }
 +func _GC()           { _GC() }
 +
 +var etext struct{}
 +
 +// Called if we receive a SIGPROF signal.
 +func sigprof(pc *uint8, sp *uint8, lr *uint8, gp *g, mp *m) {
 +      var n int32
 +      var traceback bool
 +      var stk [100]uintptr
 +
 +      if prof.hz == 0 {
 +              return
 +      }
 +
 +      // Profiling runs concurrently with GC, so it must not allocate.
 +      mp.mallocing++
 +
 +      // Define that a "user g" is a user-created goroutine, and a "system g"
 +      // is one that is m->g0 or m->gsignal. We've only made sure that we
 +      // can unwind user g's, so exclude the system g's.
 +      //
 +      // It is not quite as easy as testing gp == m->curg (the current user g)
 +      // because we might be interrupted for profiling halfway through a
 +      // goroutine switch. The switch involves updating three (or four) values:
 +      // g, PC, SP, and (on arm) LR. The PC must be the last to be updated,
 +      // because once it gets updated the new g is running.
 +      //
 +      // When switching from a user g to a system g, LR is not considered live,
 +      // so the update only affects g, SP, and PC. Since PC must be last, there
 +      // the possible partial transitions in ordinary execution are (1) g alone is updated,
 +      // (2) both g and SP are updated, and (3) SP alone is updated.
 +      // If g is updated, we'll see a system g and not look closer.
 +      // If SP alone is updated, we can detect the partial transition by checking
 +      // whether the SP is within g's stack bounds. (We could also require that SP
 +      // be changed only after g, but the stack bounds check is needed by other
 +      // cases, so there is no need to impose an additional requirement.)
 +      //
 +      // There is one exceptional transition to a system g, not in ordinary execution.
 +      // When a signal arrives, the operating system starts the signal handler running
 +      // with an updated PC and SP. The g is updated last, at the beginning of the
 +      // handler. There are two reasons this is okay. First, until g is updated the
 +      // g and SP do not match, so the stack bounds check detects the partial transition.
 +      // Second, signal handlers currently run with signals disabled, so a profiling
 +      // signal cannot arrive during the handler.
 +      //
 +      // When switching from a system g to a user g, there are three possibilities.
 +      //
 +      // First, it may be that the g switch has no PC update, because the SP
 +      // either corresponds to a user g throughout (as in asmcgocall)
 +      // or because it has been arranged to look like a user g frame
 +      // (as in cgocallback_gofunc). In this case, since the entire
 +      // transition is a g+SP update, a partial transition updating just one of
 +      // those will be detected by the stack bounds check.
 +      //
 +      // Second, when returning from a signal handler, the PC and SP updates
 +      // are performed by the operating system in an atomic update, so the g
 +      // update must be done before them. The stack bounds check detects
 +      // the partial transition here, and (again) signal handlers run with signals
 +      // disabled, so a profiling signal cannot arrive then anyway.
 +      //
 +      // Third, the common case: it may be that the switch updates g, SP, and PC
 +      // separately, as in gogo.
 +      //
 +      // Because gogo is the only instance, we check whether the PC lies
 +      // within that function, and if so, not ask for a traceback. This approach
 +      // requires knowing the size of the gogo function, which we
 +      // record in arch_*.h and check in runtime_test.go.
 +      //
 +      // There is another apparently viable approach, recorded here in case
 +      // the "PC within gogo" check turns out not to be usable.
 +      // It would be possible to delay the update of either g or SP until immediately
 +      // before the PC update instruction. Then, because of the stack bounds check,
 +      // the only problematic interrupt point is just before that PC update instruction,
 +      // and the sigprof handler can detect that instruction and simulate stepping past
 +      // it in order to reach a consistent state. On ARM, the update of g must be made
 +      // in two places (in R10 and also in a TLS slot), so the delayed update would
 +      // need to be the SP update. The sigprof handler must read the instruction at
 +      // the current PC and if it was the known instruction (for example, JMP BX or
 +      // MOV R2, PC), use that other register in place of the PC value.
 +      // The biggest drawback to this solution is that it requires that we can tell
 +      // whether it's safe to read from the memory pointed at by PC.
 +      // In a correct program, we can test PC == nil and otherwise read,
 +      // but if a profiling signal happens at the instant that a program executes
 +      // a bad jump (before the program manages to handle the resulting fault)
 +      // the profiling handler could fault trying to read nonexistent memory.
 +      //
 +      // To recap, there are no constraints on the assembly being used for the
 +      // transition. We simply require that g and SP match and that the PC is not
 +      // in gogo.
 +      traceback = true
 +      usp := uintptr(unsafe.Pointer(sp))
 +      gogo := funcPC(gogo)
 +      if gp == nil || gp != mp.curg ||
 +              usp < gp.stack.lo || gp.stack.hi < usp ||
 +              (gogo <= uintptr(unsafe.Pointer(pc)) && uintptr(unsafe.Pointer(pc)) < gogo+_RuntimeGogoBytes) {
 +              traceback = false
 +      }
 +
 +      n = 0
 +      if traceback {
 +              n = int32(gentraceback(uintptr(unsafe.Pointer(pc)), uintptr(unsafe.Pointer(sp)), uintptr(unsafe.Pointer(lr)), gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap))
 +      }
 +      if !traceback || n <= 0 {
 +              // Normal traceback is impossible or has failed.
 +              // See if it falls into several common cases.
 +              n = 0
 +              if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 {
 +                      // Cgo, we can't unwind and symbolize arbitrary C code,
 +                      // so instead collect Go stack that leads to the cgo call.
 +                      // This is especially important on windows, since all syscalls are cgo calls.
 +                      n = int32(gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[0], len(stk), nil, nil, 0))
 +              }
 +              if GOOS == "windows" && n == 0 && mp.libcallg != nil && mp.libcallpc != 0 && mp.libcallsp != 0 {
 +                      // Libcall, i.e. runtime syscall on windows.
 +                      // Collect Go stack that leads to the call.
 +                      n = int32(gentraceback(mp.libcallpc, mp.libcallsp, 0, mp.libcallg, 0, &stk[0], len(stk), nil, nil, 0))
 +              }
 +              if n == 0 {
 +                      // If all of the above has failed, account it against abstract "System" or "GC".
 +                      n = 2
 +                      // "ExternalCode" is better than "etext".
 +                      if uintptr(unsafe.Pointer(pc)) > uintptr(unsafe.Pointer(&etext)) {
 +                              pc = (*uint8)(unsafe.Pointer(uintptr(funcPC(_ExternalCode) + _PCQuantum)))
 +                      }
 +                      stk[0] = uintptr(unsafe.Pointer(pc))
 +                      if mp.gcing != 0 || mp.helpgc != 0 {
 +                              stk[1] = funcPC(_GC) + _PCQuantum
 +                      } else {
 +                              stk[1] = funcPC(_System) + _PCQuantum
 +                      }
 +              }
 +      }
 +
 +      if prof.hz != 0 {
 +              // Simple cas-lock to coordinate with setcpuprofilerate.
 +              for !cas(&prof.lock, 0, 1) {
 +                      osyield()
 +              }
 +              if prof.hz != 0 {
 +                      cpuproftick(&stk[0], n)
 +              }
 +              atomicstore(&prof.lock, 0)
 +      }
 +      mp.mallocing--
 +}
 +
 +// Arrange to call fn with a traceback hz times a second.
 +func setcpuprofilerate_m(hz int32) {
 +      // Force sane arguments.
 +      if hz < 0 {
 +              hz = 0
 +      }
 +
 +      // Disable preemption, otherwise we can be rescheduled to another thread
 +      // that has profiling enabled.
 +      _g_ := getg()
 +      _g_.m.locks++
 +
 +      // Stop profiler on this thread so that it is safe to lock prof.
 +      // if a profiling signal came in while we had prof locked,
 +      // it would deadlock.
 +      resetcpuprofiler(0)
 +
 +      for !cas(&prof.lock, 0, 1) {
 +              osyield()
 +      }
 +      prof.hz = hz
 +      atomicstore(&prof.lock, 0)
 +
 +      lock(&sched.lock)
 +      sched.profilehz = hz
 +      unlock(&sched.lock)
 +
 +      if hz != 0 {
 +              resetcpuprofiler(hz)
 +      }
 +
 +      _g_.m.locks--
 +}
 +
 +// Change number of processors.  The world is stopped, sched is locked.
 +func procresize(new int32) {
 +      old := gomaxprocs
 +      if old < 0 || old > _MaxGomaxprocs || new <= 0 || new > _MaxGomaxprocs {
 +              gothrow("procresize: invalid arg")
 +      }
 +
 +      // initialize new P's
 +      for i := int32(0); i < new; i++ {
 +              p := allp[i]
 +              if p == nil {
 +                      p = newP()
 +                      p.id = i
 +                      p.status = _Pgcstop
 +                      atomicstorep(unsafe.Pointer(&allp[i]), unsafe.Pointer(p))
 +              }
 +              if p.mcache == nil {
 +                      if old == 0 && i == 0 {
 +                              if getg().m.mcache == nil {
 +                                      gothrow("missing mcache?")
 +                              }
 +                              p.mcache = getg().m.mcache // bootstrap
 +                      } else {
 +                              p.mcache = allocmcache()
 +                      }
 +              }
 +      }
 +
 +      // redistribute runnable G's evenly
 +      // collect all runnable goroutines in global queue preserving FIFO order
 +      // FIFO order is required to ensure fairness even during frequent GCs
 +      // see http://golang.org/issue/7126
 +      empty := false
 +      for !empty {
 +              empty = true
 +              for i := int32(0); i < old; i++ {
 +                      p := allp[i]
 +                      if p.runqhead == p.runqtail {
 +                              continue
 +                      }
 +                      empty = false
 +                      // pop from tail of local queue
 +                      p.runqtail--
 +                      gp := p.runq[p.runqtail%uint32(len(p.runq))]
 +                      // push onto head of global queue
 +                      gp.schedlink = sched.runqhead
 +                      sched.runqhead = gp
 +                      if sched.runqtail == nil {
 +                              sched.runqtail = gp
 +                      }
 +                      sched.runqsize++
 +              }
 +      }
 +
 +      // fill local queues with at most len(p.runq)/2 goroutines
 +      // start at 1 because current M already executes some G and will acquire allp[0] below,
 +      // so if we have a spare G we want to put it into allp[1].
 +      var _p_ p
 +      for i := int32(1); i < new*int32(len(_p_.runq))/2 && sched.runqsize > 0; i++ {
 +              gp := sched.runqhead
 +              sched.runqhead = gp.schedlink
 +              if sched.runqhead == nil {
 +                      sched.runqtail = nil
 +              }
 +              sched.runqsize--
 +              runqput(allp[i%new], gp)
 +      }
 +
 +      // free unused P's
 +      for i := new; i < old; i++ {
 +              p := allp[i]
 +              freemcache(p.mcache)
 +              p.mcache = nil
 +              gfpurge(p)
 +              p.status = _Pdead
 +              // can't free P itself because it can be referenced by an M in syscall
 +      }
 +
 +      _g_ := getg()
 +      if _g_.m.p != nil {
 +              _g_.m.p.m = nil
 +      }
 +      _g_.m.p = nil
 +      _g_.m.mcache = nil
 +      p := allp[0]
 +      p.m = nil
 +      p.status = _Pidle
 +      acquirep(p)
 +      for i := new - 1; i > 0; i-- {
 +              p := allp[i]
 +              p.status = _Pidle
 +              pidleput(p)
 +      }
 +      var int32p *int32 = &gomaxprocs // make compiler check that gomaxprocs is an int32
 +      atomicstore((*uint32)(unsafe.Pointer(int32p)), uint32(new))
 +}
 +
 +// Associate p and the current m.
 +func acquirep(_p_ *p) {
 +      _g_ := getg()
 +
 +      if _g_.m.p != nil || _g_.m.mcache != nil {
 +              gothrow("acquirep: already in go")
 +      }
 +      if _p_.m != nil || _p_.status != _Pidle {
 +              id := int32(0)
 +              if _p_.m != nil {
 +                      id = _p_.m.id
 +              }
 +              print("acquirep: p->m=", _p_.m, "(", id, ") p->status=", _p_.status, "\n")
 +              gothrow("acquirep: invalid p state")
 +      }
 +      _g_.m.mcache = _p_.mcache
 +      _g_.m.p = _p_
 +      _p_.m = _g_.m
 +      _p_.status = _Prunning
 +}
 +
 +// Disassociate p and the current m.
 +func releasep() *p {
 +      _g_ := getg()
 +
 +      if _g_.m.p == nil || _g_.m.mcache == nil {
 +              gothrow("releasep: invalid arg")
 +      }
 +      _p_ := _g_.m.p
 +      if _p_.m != _g_.m || _p_.mcache != _g_.m.mcache || _p_.status != _Prunning {
 +              print("releasep: m=", _g_.m, " m->p=", _g_.m.p, " p->m=", _p_.m, " m->mcache=", _g_.m.mcache, " p->mcache=", _p_.mcache, " p->status=", _p_.status, "\n")
 +              gothrow("releasep: invalid p state")
 +      }
 +      _g_.m.p = nil
 +      _g_.m.mcache = nil
 +      _p_.m = nil
 +      _p_.status = _Pidle
 +      return _p_
 +}
 +
 +func incidlelocked(v int32) {
 +      lock(&sched.lock)
 +      sched.nmidlelocked += v
 +      if v > 0 {
 +              checkdead()
 +      }
 +      unlock(&sched.lock)
 +}
 +
 +// Check for deadlock situation.
 +// The check is based on number of running M's, if 0 -> deadlock.
 +func checkdead() {
 +      // If we are dying because of a signal caught on an already idle thread,
 +      // freezetheworld will cause all running threads to block.
 +      // And runtime will essentially enter into deadlock state,
 +      // except that there is a thread that will call exit soon.
 +      if panicking > 0 {
 +              return
 +      }
 +
 +      // -1 for sysmon
 +      run := sched.mcount - sched.nmidle - sched.nmidlelocked - 1
 +      if run > 0 {
 +              return
 +      }
 +      if run < 0 {
 +              print("runtime: checkdead: nmidle=", sched.nmidle, " nmidlelocked=", sched.nmidlelocked, " mcount=", sched.mcount, "\n")
 +              gothrow("checkdead: inconsistent counts")
 +      }
 +
 +      grunning := 0
 +      lock(&allglock)
 +      for i := 0; i < len(allgs); i++ {
 +              gp := allgs[i]
 +              if gp.issystem {
 +                      continue
 +              }
 +              s := readgstatus(gp)
 +              switch s &^ _Gscan {
 +              case _Gwaiting:
 +                      grunning++
 +              case _Grunnable,
 +                      _Grunning,
 +                      _Gsyscall:
 +                      unlock(&allglock)
 +                      print("runtime: checkdead: find g ", gp.goid, " in status ", s, "\n")
 +                      gothrow("checkdead: runnable g")
 +              }
 +      }
 +      unlock(&allglock)
 +      if grunning == 0 { // possible if main goroutine calls runtime·Goexit()
 +              gothrow("no goroutines (main called runtime.Goexit) - deadlock!")
 +      }
 +
 +      // Maybe jump time forward for playground.
 +      gp := timejump()
 +      if gp != nil {
 +              casgstatus(gp, _Gwaiting, _Grunnable)
 +              globrunqput(gp)
 +              _p_ := pidleget()
 +              if _p_ == nil {
 +                      gothrow("checkdead: no p for timer")
 +              }
 +              mp := mget()
 +              if mp == nil {
 +                      _newm(nil, _p_)
 +              } else {
 +                      mp.nextp = _p_
 +                      notewakeup(&mp.park)
 +              }
 +              return
 +      }
 +
 +      getg().m.throwing = -1 // do not dump full stacks
 +      gothrow("all goroutines are asleep - deadlock!")
 +}
 +
 +func sysmon() {
 +      // If we go two minutes without a garbage collection, force one to run.
 +      forcegcperiod := int64(2 * 60 * 1e9)
 +
 +      // If a heap span goes unused for 5 minutes after a garbage collection,
 +      // we hand it back to the operating system.
 +      scavengelimit := int64(5 * 60 * 1e9)
 +
 +      if debug.scavenge > 0 {
 +              // Scavenge-a-lot for testing.
 +              forcegcperiod = 10 * 1e6
 +              scavengelimit = 20 * 1e6
 +      }
 +
 +      lastscavenge := nanotime()
 +      nscavenge := 0
 +
 +      // Make wake-up period small enough for the sampling to be correct.
 +      maxsleep := forcegcperiod / 2
 +      if scavengelimit < forcegcperiod {
 +              maxsleep = scavengelimit / 2
 +      }
 +
 +      lasttrace := int64(0)
 +      idle := 0 // how many cycles in succession we had not wokeup somebody
 +      delay := uint32(0)
 +      for {
 +              if idle == 0 { // start with 20us sleep...
 +                      delay = 20
 +              } else if idle > 50 { // start doubling the sleep after 1ms...
 +                      delay *= 2
 +              }
 +              if delay > 10*1000 { // up to 10ms
 +                      delay = 10 * 1000
 +              }
 +              usleep(delay)
 +              if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomicload(&sched.npidle) == uint32(gomaxprocs)) { // TODO: fast atomic
 +                      lock(&sched.lock)
 +                      if atomicload(&sched.gcwaiting) != 0 || atomicload(&sched.npidle) == uint32(gomaxprocs) {
 +                              atomicstore(&sched.sysmonwait, 1)
 +                              unlock(&sched.lock)
 +                              notetsleep(&sched.sysmonnote, maxsleep)
 +                              lock(&sched.lock)
 +                              atomicstore(&sched.sysmonwait, 0)
 +                              noteclear(&sched.sysmonnote)
 +                              idle = 0
 +                              delay = 20
 +                      }
 +                      unlock(&sched.lock)
 +              }
 +              // poll network if not polled for more than 10ms
 +              lastpoll := int64(atomicload64(&sched.lastpoll))
 +              now := nanotime()
 +              unixnow := unixnanotime()
 +              if lastpoll != 0 && lastpoll+10*1000*1000 < now {
 +                      cas64(&sched.lastpoll, uint64(lastpoll), uint64(now))
 +                      gp := netpoll(false) // non-blocking - returns list of goroutines
 +                      if gp != nil {
 +                              // Need to decrement number of idle locked M's
 +                              // (pretending that one more is running) before injectglist.
 +                              // Otherwise it can lead to the following situation:
 +                              // injectglist grabs all P's but before it starts M's to run the P's,
 +                              // another M returns from syscall, finishes running its G,
 +                              // observes that there is no work to do and no other running M's
 +                              // and reports deadlock.
 +                              incidlelocked(-1)
 +                              injectglist(gp)
 +                              incidlelocked(1)
 +                      }
 +              }
 +              // retake P's blocked in syscalls
 +              // and preempt long running G's
 +              if retake(now) != 0 {
 +                      idle = 0
 +              } else {
 +                      idle++
 +              }
 +              // check if we need to force a GC
 +              lastgc := int64(atomicload64(&memstats.last_gc))
 +              if lastgc != 0 && unixnow-lastgc > forcegcperiod && atomicload(&forcegc.idle) != 0 {
 +                      lock(&forcegc.lock)
 +                      forcegc.idle = 0
 +                      forcegc.g.schedlink = nil
 +                      injectglist(forcegc.g)
 +                      unlock(&forcegc.lock)
 +              }
 +              // scavenge heap once in a while
 +              if lastscavenge+scavengelimit/2 < now {
 +                      mHeap_Scavenge(int32(nscavenge), uint64(now), uint64(scavengelimit))
 +                      lastscavenge = now
 +                      nscavenge++
 +              }
 +              if debug.schedtrace > 0 && lasttrace+int64(debug.schedtrace*1000000) <= now {
 +                      lasttrace = now
 +                      schedtrace(debug.scheddetail > 0)
 +              }
 +      }
 +}
 +
 +var pdesc [_MaxGomaxprocs]struct {
 +      schedtick   uint32
 +      schedwhen   int64
 +      syscalltick uint32
 +      syscallwhen int64
 +}
 +
 +func retake(now int64) uint32 {
 +      n := 0
 +      for i := int32(0); i < gomaxprocs; i++ {
 +              _p_ := allp[i]
 +              if _p_ == nil {
 +                      continue
 +              }
 +              pd := &pdesc[i]
 +              s := _p_.status
 +              if s == _Psyscall {
 +                      // Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).
 +                      t := int64(_p_.syscalltick)
 +                      if int64(pd.syscalltick) != t {
 +                              pd.syscalltick = uint32(t)
 +                              pd.syscallwhen = now
 +                              continue
 +                      }
 +                      // On the one hand we don't want to retake Ps if there is no other work to do,
 +                      // but on the other hand we want to retake them eventually
 +                      // because they can prevent the sysmon thread from deep sleep.
 +                      if _p_.runqhead == _p_.runqtail && atomicload(&sched.nmspinning)+atomicload(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now {
 +                              continue
 +                      }
 +                      // Need to decrement number of idle locked M's
 +                      // (pretending that one more is running) before the CAS.
 +                      // Otherwise the M from which we retake can exit the syscall,
 +                      // increment nmidle and report deadlock.
 +                      incidlelocked(-1)
 +                      if cas(&_p_.status, s, _Pidle) {
 +                              n++
 +                              handoffp(_p_)
 +                      }
 +                      incidlelocked(1)
 +              } else if s == _Prunning {
 +                      // Preempt G if it's running for more than 10ms.
 +                      t := int64(_p_.schedtick)
 +                      if int64(pd.schedtick) != t {
 +                              pd.schedtick = uint32(t)
 +                              pd.schedwhen = now
 +                              continue
 +                      }
 +                      if pd.schedwhen+10*1000*1000 > now {
 +                              continue
 +                      }
 +                      preemptone(_p_)
 +              }
 +      }
 +      return uint32(n)
 +}
 +
 +// Tell all goroutines that they have been preempted and they should stop.
 +// This function is purely best-effort.  It can fail to inform a goroutine if a
 +// processor just started running it.
 +// No locks need to be held.
 +// Returns true if preemption request was issued to at least one goroutine.
 +func preemptall() bool {
 +      res := false
 +      for i := int32(0); i < gomaxprocs; i++ {
 +              _p_ := allp[i]
 +              if _p_ == nil || _p_.status != _Prunning {
 +                      continue
 +              }
 +              if preemptone(_p_) {
 +                      res = true
 +              }
 +      }
 +      return res
 +}
 +
 +// Tell the goroutine running on processor P to stop.
 +// This function is purely best-effort.  It can incorrectly fail to inform the
 +// goroutine.  It can send inform the wrong goroutine.  Even if it informs the
 +// correct goroutine, that goroutine might ignore the request if it is
 +// simultaneously executing newstack.
 +// No lock needs to be held.
 +// Returns true if preemption request was issued.
 +// The actual preemption will happen at some point in the future
 +// and will be indicated by the gp->status no longer being
 +// Grunning
 +func preemptone(_p_ *p) bool {
 +      mp := _p_.m
 +      if mp == nil || mp == getg().m {
 +              return false
 +      }
 +      gp := mp.curg
 +      if gp == nil || gp == mp.g0 {
 +              return false
 +      }
 +
 +      gp.preempt = true
 +
 +      // Every call in a go routine checks for stack overflow by
 +      // comparing the current stack pointer to gp->stackguard0.
 +      // Setting gp->stackguard0 to StackPreempt folds
 +      // preemption into the normal stack overflow check.
 +      gp.stackguard0 = stackPreempt
 +      return true
 +}
 +
 +var starttime int64
 +
 +func schedtrace(detailed bool) {
 +      now := nanotime()
 +      if starttime == 0 {
 +              starttime = now
 +      }
 +
 +      lock(&sched.lock)
 +      print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle, " threads=", sched.mcount, " spinningthreads=", sched.nmspinning, " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize)
 +      if detailed {
 +              print(" gcwaiting=", sched.gcwaiting, " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait, "\n")
 +      }
 +      // We must be careful while reading data from P's, M's and G's.
 +      // Even if we hold schedlock, most data can be changed concurrently.
 +      // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
 +      for i := int32(0); i < gomaxprocs; i++ {
 +              _p_ := allp[i]
 +              if _p_ == nil {
 +                      continue
 +              }
 +              mp := _p_.m
 +              h := atomicload(&_p_.runqhead)
 +              t := atomicload(&_p_.runqtail)
 +              if detailed {
 +                      id := int32(-1)
 +                      if mp != nil {
 +                              id = mp.id
 +                      }
 +                      print("  P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gfreecnt, "\n")
 +              } else {
 +                      // In non-detailed mode format lengths of per-P run queues as:
 +                      // [len1 len2 len3 len4]
 +                      print(" ")
 +                      if i == 0 {
 +                              print("[")
 +                      }
 +                      print(t - h)
 +                      if i == gomaxprocs-1 {
 +                              print("]\n")
 +                      }
 +              }
 +      }
 +
 +      if !detailed {
 +              unlock(&sched.lock)
 +              return
 +      }
 +
 +      for mp := allm; mp != nil; mp = mp.alllink {
 +              _p_ := mp.p
 +              gp := mp.curg
 +              lockedg := mp.lockedg
 +              id1 := int32(-1)
 +              if _p_ != nil {
 +                      id1 = _p_.id
 +              }
 +              id2 := int64(-1)
 +              if gp != nil {
 +                      id2 = gp.goid
 +              }
 +              id3 := int64(-1)
 +              if lockedg != nil {
 +                      id3 = lockedg.goid
 +              }
 +              print("  M", mp.id, ": p=", id1, " curg=", id2, " mallocing=", mp.mallocing, " throwing=", mp.throwing, " gcing=", mp.gcing, ""+" locks=", mp.locks, " dying=", mp.dying, " helpgc=", mp.helpgc, " spinning=", mp.spinning, " blocked=", getg().m.blocked, " lockedg=", id3, "\n")
 +      }
 +
 +      lock(&allglock)
 +      for gi := 0; gi < len(allgs); gi++ {
 +              gp := allgs[gi]
 +              mp := gp.m
 +              lockedm := gp.lockedm
 +              id1 := int32(-1)
 +              if mp != nil {
 +                      id1 = mp.id
 +              }
 +              id2 := int32(-1)
 +              if lockedm != nil {
 +                      id2 = lockedm.id
 +              }
 +              print("  G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n")
 +      }
 +      unlock(&allglock)
 +      unlock(&sched.lock)
 +}
 +
 +// Put mp on midle list.
 +// Sched must be locked.
 +func mput(mp *m) {
 +      mp.schedlink = sched.midle
 +      sched.midle = mp
 +      sched.nmidle++
 +      checkdead()
 +}
 +
 +// Try to get an m from midle list.
 +// Sched must be locked.
 +func mget() *m {
 +      mp := sched.midle
 +      if mp != nil {
 +              sched.midle = mp.schedlink
 +              sched.nmidle--
 +      }
 +      return mp
 +}
 +
 +// Put gp on the global runnable queue.
 +// Sched must be locked.
 +func globrunqput(gp *g) {
 +      gp.schedlink = nil
 +      if sched.runqtail != nil {
 +              sched.runqtail.schedlink = gp
 +      } else {
 +              sched.runqhead = gp
 +      }
 +      sched.runqtail = gp
 +      sched.runqsize++
 +}
 +
 +// Put a batch of runnable goroutines on the global runnable queue.
 +// Sched must be locked.
 +func globrunqputbatch(ghead *g, gtail *g, n int32) {
 +      gtail.schedlink = nil
 +      if sched.runqtail != nil {
 +              sched.runqtail.schedlink = ghead
 +      } else {
 +              sched.runqhead = ghead
 +      }
 +      sched.runqtail = gtail
 +      sched.runqsize += n
 +}
 +
 +// Try get a batch of G's from the global runnable queue.
 +// Sched must be locked.
 +func globrunqget(_p_ *p, max int32) *g {
 +      if sched.runqsize == 0 {
 +              return nil
 +      }
 +
 +      n := sched.runqsize/gomaxprocs + 1
 +      if n > sched.runqsize {
 +              n = sched.runqsize
 +      }
 +      if max > 0 && n > max {
 +              n = max
 +      }
 +      if n > int32(len(_p_.runq))/2 {
 +              n = int32(len(_p_.runq)) / 2
 +      }
 +
 +      sched.runqsize -= n
 +      if sched.runqsize == 0 {
 +              sched.runqtail = nil
 +      }
 +
 +      gp := sched.runqhead
 +      sched.runqhead = gp.schedlink
 +      n--
 +      for ; n > 0; n-- {
 +              gp1 := sched.runqhead
 +              sched.runqhead = gp1.schedlink
 +              runqput(_p_, gp1)
 +      }
 +      return gp
 +}
 +
 +// Put p to on _Pidle list.
 +// Sched must be locked.
 +func pidleput(_p_ *p) {
 +      _p_.link = sched.pidle
 +      sched.pidle = _p_
 +      xadd(&sched.npidle, 1) // TODO: fast atomic
 +}
 +
 +// Try get a p from _Pidle list.
 +// Sched must be locked.
 +func pidleget() *p {
 +      _p_ := sched.pidle
 +      if _p_ != nil {
 +              sched.pidle = _p_.link
 +              xadd(&sched.npidle, -1) // TODO: fast atomic
 +      }
 +      return _p_
 +}
 +
 +// Try to put g on local runnable queue.
 +// If it's full, put onto global queue.
 +// Executed only by the owner P.
 +func runqput(_p_ *p, gp *g) {
 +retry:
 +      h := atomicload(&_p_.runqhead) // load-acquire, synchronize with consumers
 +      t := _p_.runqtail
 +      if t-h < uint32(len(_p_.runq)) {
 +              _p_.runq[t%uint32(len(_p_.runq))] = gp
 +              atomicstore(&_p_.runqtail, t+1) // store-release, makes the item available for consumption
 +              return
 +      }
 +      if runqputslow(_p_, gp, h, t) {
 +              return
 +      }
 +      // the queue is not full, now the put above must suceed
 +      goto retry
 +}
 +
 +// Put g and a batch of work from local runnable queue on global queue.
 +// Executed only by the owner P.
 +func runqputslow(_p_ *p, gp *g, h, t uint32) bool {
 +      var batch [len(_p_.runq)/2 + 1]*g
 +
 +      // First, grab a batch from local queue.
 +      n := t - h
 +      n = n / 2
 +      if n != uint32(len(_p_.runq)/2) {
 +              gothrow("runqputslow: queue is not full")
 +      }
 +      for i := uint32(0); i < n; i++ {
 +              batch[i] = _p_.runq[(h+i)%uint32(len(_p_.runq))]
 +      }
 +      if !cas(&_p_.runqhead, h, h+n) { // cas-release, commits consume
 +              return false
 +      }
 +      batch[n] = gp
 +
 +      // Link the goroutines.
 +      for i := uint32(0); i < n; i++ {
 +              batch[i].schedlink = batch[i+1]
 +      }
 +
 +      // Now put the batch on global queue.
 +      lock(&sched.lock)
 +      globrunqputbatch(batch[0], batch[n], int32(n+1))
 +      unlock(&sched.lock)
 +      return true
 +}
 +
 +// Get g from local runnable queue.
 +// Executed only by the owner P.
 +func runqget(_p_ *p) *g {
 +      for {
 +              h := atomicload(&_p_.runqhead) // load-acquire, synchronize with other consumers
 +              t := _p_.runqtail
 +              if t == h {
 +                      return nil
 +              }
 +              gp := _p_.runq[h%uint32(len(_p_.runq))]
 +              if cas(&_p_.runqhead, h, h+1) { // cas-release, commits consume
 +                      return gp
 +              }
 +      }
 +}
 +
 +// Grabs a batch of goroutines from local runnable queue.
 +// batch array must be of size len(p->runq)/2. Returns number of grabbed goroutines.
 +// Can be executed by any P.
 +func runqgrab(_p_ *p, batch []*g) uint32 {
 +      for {
 +              h := atomicload(&_p_.runqhead) // load-acquire, synchronize with other consumers
 +              t := atomicload(&_p_.runqtail) // load-acquire, synchronize with the producer
 +              n := t - h
 +              n = n - n/2
 +              if n == 0 {
 +                      return 0
 +              }
 +              if n > uint32(len(_p_.runq)/2) { // read inconsistent h and t
 +                      continue
 +              }
 +              for i := uint32(0); i < n; i++ {
 +                      batch[i] = _p_.runq[(h+i)%uint32(len(_p_.runq))]
 +              }
 +              if cas(&_p_.runqhead, h, h+n) { // cas-release, commits consume
 +                      return n
 +              }
 +      }
 +}
 +
 +// Steal half of elements from local runnable queue of p2
 +// and put onto local runnable queue of p.
 +// Returns one of the stolen elements (or nil if failed).
 +func runqsteal(_p_, p2 *p) *g {
 +      var batch [len(_p_.runq) / 2]*g
 +
 +      n := runqgrab(p2, batch[:])
 +      if n == 0 {
 +              return nil
 +      }
 +      n--
 +      gp := batch[n]
 +      if n == 0 {
 +              return gp
 +      }
 +      h := atomicload(&_p_.runqhead) // load-acquire, synchronize with consumers
 +      t := _p_.runqtail
 +      if t-h+n >= uint32(len(_p_.runq)) {
 +              gothrow("runqsteal: runq overflow")
 +      }
 +      for i := uint32(0); i < n; i++ {
 +              _p_.runq[(t+i)%uint32(len(_p_.runq))] = batch[i]
 +      }
 +      atomicstore(&_p_.runqtail, t+n) // store-release, makes the item available for consumption
 +      return gp
 +}
 +
 +func testSchedLocalQueue() {
 +      _p_ := new(p)
 +      gs := make([]g, len(_p_.runq))
 +      for i := 0; i < len(_p_.runq); i++ {
 +              if runqget(_p_) != nil {
 +                      gothrow("runq is not empty initially")
 +              }
 +              for j := 0; j < i; j++ {
 +                      runqput(_p_, &gs[i])
 +              }
 +              for j := 0; j < i; j++ {
 +                      if runqget(_p_) != &gs[i] {
 +                              print("bad element at iter ", i, "/", j, "\n")
 +                              gothrow("bad element")
 +                      }
 +              }
 +              if runqget(_p_) != nil {
 +                      gothrow("runq is not empty afterwards")
 +              }
 +      }
 +}
 +
 +func testSchedLocalQueueSteal() {
 +      p1 := new(p)
 +      p2 := new(p)
 +      gs := make([]g, len(p1.runq))
 +      for i := 0; i < len(p1.runq); i++ {
 +              for j := 0; j < i; j++ {
 +                      gs[j].sig = 0
 +                      runqput(p1, &gs[j])
 +              }
 +              gp := runqsteal(p2, p1)
 +              s := 0
 +              if gp != nil {
 +                      s++
 +                      gp.sig++
 +              }
 +              for {
 +                      gp = runqget(p2)
 +                      if gp == nil {
 +                              break
 +                      }
 +                      s++
 +                      gp.sig++
 +              }
 +              for {
 +                      gp = runqget(p1)
 +                      if gp == nil {
 +                              break
 +                      }
 +                      gp.sig++
 +              }
 +              for j := 0; j < i; j++ {
 +                      if gs[j].sig != 1 {
 +                              print("bad element ", j, "(", gs[j].sig, ") at iter ", i, "\n")
 +                              gothrow("bad element")
 +                      }
 +              }
 +              if s != i/2 && s != i/2+1 {
 +                      print("bad steal ", s, ", want ", i/2, " or ", i/2+1, ", iter ", i, "\n")
 +                      gothrow("bad steal")
 +              }
 +      }
 +}
 +
 +func setMaxThreads(in int) (out int) {
 +      lock(&sched.lock)
 +      out = int(sched.maxmcount)
 +      sched.maxmcount = int32(in)
 +      checkmcount()
 +      unlock(&sched.lock)
 +      return
 +}
 +
 +var goexperiment string = "GOEXPERIMENT" // TODO: defined in zaexperiment.h
 +
 +func haveexperiment(name string) bool {
 +      x := goexperiment
 +      for x != "" {
 +              xname := ""
 +              i := index(x, ",")
 +              if i < 0 {
 +                      xname, x = x, ""
 +              } else {
 +                      xname, x = x[:i], x[i+1:]
 +              }
 +              if xname == name {
 +                      return true
 +              }
 +      }
 +      return false
 +}
 +
 +//go:nosplit
 +func sync_procPin() int {
 +      _g_ := getg()
 +      mp := _g_.m
 +
 +      mp.locks++
 +      return int(mp.p.id)
 +}
 +
 +//go:nosplit
 +func sync_procUnpin() {
 +      _g_ := getg()
 +      _g_.m.locks--
 +}
index ad83e589512ce5e9d36db9a203939dc4d3b384b4,0000000000000000000000000000000000000000..1fd61ce1a28d84c2f77e1437bf47d24e6e5a4754
mode 100644,000000..100644
--- /dev/null
@@@ -1,807 -1,0 +1,801 @@@
-       oldstatus := readgstatus(gp)
-       oldstatus &^= _Gscan
-       if oldstatus == _Gwaiting || oldstatus == _Grunnable {
-               casgstatus(gp, oldstatus, _Gcopystack) // oldstatus is Gwaiting or Grunnable
-       } else {
-               gothrow("copystack: bad status, not Gwaiting or Grunnable")
-       }
 +// Copyright 2013 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"
 +
 +const (
 +      // StackDebug == 0: no logging
 +      //            == 1: logging of per-stack operations
 +      //            == 2: logging of per-frame operations
 +      //            == 3: logging of per-word updates
 +      //            == 4: logging of per-word reads
 +      stackDebug       = 0
 +      stackFromSystem  = 0 // allocate stacks from system memory instead of the heap
 +      stackFaultOnFree = 0 // old stacks are mapped noaccess to detect use after free
 +      stackPoisonCopy  = 0 // fill stack that should not be accessed with garbage, to detect bad dereferences during copy
 +
 +      stackCache = 1
 +)
 +
 +const (
 +      uintptrMask = 1<<(8*ptrSize) - 1
 +      poisonGC    = uintptrMask & 0xf969696969696969
 +      poisonStack = uintptrMask & 0x6868686868686868
 +
 +      // Goroutine preemption request.
 +      // Stored into g->stackguard0 to cause split stack check failure.
 +      // Must be greater than any real sp.
 +      // 0xfffffade in hex.
 +      stackPreempt = uintptrMask & -1314
 +
 +      // Thread is forking.
 +      // Stored into g->stackguard0 to cause split stack check failure.
 +      // Must be greater than any real sp.
 +      stackFork = uintptrMask & -1234
 +)
 +
 +// Global pool of spans that have free stacks.
 +// Stacks are assigned an order according to size.
 +//     order = log_2(size/FixedStack)
 +// There is a free list for each order.
 +// TODO: one lock per order?
 +var stackpool [_NumStackOrders]mspan
 +var stackpoolmu mutex
 +
 +var stackfreequeue stack
 +
 +func stackinit() {
 +      if _StackCacheSize&_PageMask != 0 {
 +              gothrow("cache size must be a multiple of page size")
 +      }
 +      for i := range stackpool {
 +              mSpanList_Init(&stackpool[i])
 +      }
 +}
 +
 +// Allocates a stack from the free pool.  Must be called with
 +// stackpoolmu held.
 +func stackpoolalloc(order uint8) *mlink {
 +      list := &stackpool[order]
 +      s := list.next
 +      if s == list {
 +              // no free stacks.  Allocate another span worth.
 +              s = mHeap_AllocStack(&mheap_, _StackCacheSize>>_PageShift)
 +              if s == nil {
 +                      gothrow("out of memory")
 +              }
 +              if s.ref != 0 {
 +                      gothrow("bad ref")
 +              }
 +              if s.freelist != nil {
 +                      gothrow("bad freelist")
 +              }
 +              for i := uintptr(0); i < _StackCacheSize; i += _FixedStack << order {
 +                      x := (*mlink)(unsafe.Pointer(uintptr(s.start)<<_PageShift + i))
 +                      x.next = s.freelist
 +                      s.freelist = x
 +              }
 +              mSpanList_Insert(list, s)
 +      }
 +      x := s.freelist
 +      if x == nil {
 +              gothrow("span has no free stacks")
 +      }
 +      s.freelist = x.next
 +      s.ref++
 +      if s.freelist == nil {
 +              // all stacks in s are allocated.
 +              mSpanList_Remove(s)
 +      }
 +      return x
 +}
 +
 +// Adds stack x to the free pool.  Must be called with stackpoolmu held.
 +func stackpoolfree(x *mlink, order uint8) {
 +      s := mHeap_Lookup(&mheap_, (unsafe.Pointer)(x))
 +      if s.state != _MSpanStack {
 +              gothrow("freeing stack not in a stack span")
 +      }
 +      if s.freelist == nil {
 +              // s will now have a free stack
 +              mSpanList_Insert(&stackpool[order], s)
 +      }
 +      x.next = s.freelist
 +      s.freelist = x
 +      s.ref--
 +      if s.ref == 0 {
 +              // span is completely free - return to heap
 +              mSpanList_Remove(s)
 +              s.freelist = nil
 +              mHeap_FreeStack(&mheap_, s)
 +      }
 +}
 +
 +// stackcacherefill/stackcacherelease implement a global pool of stack segments.
 +// The pool is required to prevent unlimited growth of per-thread caches.
 +func stackcacherefill(c *mcache, order uint8) {
 +      if stackDebug >= 1 {
 +              print("stackcacherefill order=", order, "\n")
 +      }
 +
 +      // Grab some stacks from the global cache.
 +      // Grab half of the allowed capacity (to prevent thrashing).
 +      var list *mlink
 +      var size uintptr
 +      lock(&stackpoolmu)
 +      for size < _StackCacheSize/2 {
 +              x := stackpoolalloc(order)
 +              x.next = list
 +              list = x
 +              size += _FixedStack << order
 +      }
 +      unlock(&stackpoolmu)
 +      c.stackcache[order].list = list
 +      c.stackcache[order].size = size
 +}
 +
 +func stackcacherelease(c *mcache, order uint8) {
 +      if stackDebug >= 1 {
 +              print("stackcacherelease order=", order, "\n")
 +      }
 +      x := c.stackcache[order].list
 +      size := c.stackcache[order].size
 +      lock(&stackpoolmu)
 +      for size > _StackCacheSize/2 {
 +              y := x.next
 +              stackpoolfree(x, order)
 +              x = y
 +              size -= _FixedStack << order
 +      }
 +      unlock(&stackpoolmu)
 +      c.stackcache[order].list = x
 +      c.stackcache[order].size = size
 +}
 +
 +func stackcache_clear(c *mcache) {
 +      if stackDebug >= 1 {
 +              print("stackcache clear\n")
 +      }
 +      lock(&stackpoolmu)
 +      for order := uint8(0); order < _NumStackOrders; order++ {
 +              x := c.stackcache[order].list
 +              for x != nil {
 +                      y := x.next
 +                      stackpoolfree(x, order)
 +                      x = y
 +              }
 +              c.stackcache[order].list = nil
 +              c.stackcache[order].size = 0
 +      }
 +      unlock(&stackpoolmu)
 +}
 +
 +func stackalloc(n uint32) stack {
 +      // Stackalloc must be called on scheduler stack, so that we
 +      // never try to grow the stack during the code that stackalloc runs.
 +      // Doing so would cause a deadlock (issue 1547).
 +      thisg := getg()
 +      if thisg != thisg.m.g0 {
 +              gothrow("stackalloc not on scheduler stack")
 +      }
 +      if n&(n-1) != 0 {
 +              gothrow("stack size not a power of 2")
 +      }
 +      if stackDebug >= 1 {
 +              print("stackalloc ", n, "\n")
 +      }
 +
 +      if debug.efence != 0 || stackFromSystem != 0 {
 +              v := sysAlloc(round(uintptr(n), _PageSize), &memstats.stacks_sys)
 +              if v == nil {
 +                      gothrow("out of memory (stackalloc)")
 +              }
 +              return stack{uintptr(v), uintptr(v) + uintptr(n)}
 +      }
 +
 +      // Small stacks are allocated with a fixed-size free-list allocator.
 +      // If we need a stack of a bigger size, we fall back on allocating
 +      // a dedicated span.
 +      var v unsafe.Pointer
 +      if stackCache != 0 && n < _FixedStack<<_NumStackOrders && n < _StackCacheSize {
 +              order := uint8(0)
 +              n2 := n
 +              for n2 > _FixedStack {
 +                      order++
 +                      n2 >>= 1
 +              }
 +              var x *mlink
 +              c := thisg.m.mcache
 +              if c == nil || thisg.m.gcing != 0 || thisg.m.helpgc != 0 {
 +                      // c == nil can happen in the guts of exitsyscall or
 +                      // procresize. Just get a stack from the global pool.
 +                      // Also don't touch stackcache during gc
 +                      // as it's flushed concurrently.
 +                      lock(&stackpoolmu)
 +                      x = stackpoolalloc(order)
 +                      unlock(&stackpoolmu)
 +              } else {
 +                      x = c.stackcache[order].list
 +                      if x == nil {
 +                              stackcacherefill(c, order)
 +                              x = c.stackcache[order].list
 +                      }
 +                      c.stackcache[order].list = x.next
 +                      c.stackcache[order].size -= uintptr(n)
 +              }
 +              v = (unsafe.Pointer)(x)
 +      } else {
 +              s := mHeap_AllocStack(&mheap_, round(uintptr(n), _PageSize)>>_PageShift)
 +              if s == nil {
 +                      gothrow("out of memory")
 +              }
 +              v = (unsafe.Pointer)(s.start << _PageShift)
 +      }
 +
 +      if raceenabled {
 +              racemalloc(v, uintptr(n))
 +      }
 +      if stackDebug >= 1 {
 +              print("  allocated ", v, "\n")
 +      }
 +      return stack{uintptr(v), uintptr(v) + uintptr(n)}
 +}
 +
 +func stackfree(stk stack) {
 +      gp := getg()
 +      n := stk.hi - stk.lo
 +      v := (unsafe.Pointer)(stk.lo)
 +      if n&(n-1) != 0 {
 +              gothrow("stack not a power of 2")
 +      }
 +      if stackDebug >= 1 {
 +              println("stackfree", v, n)
 +              memclr(v, n) // for testing, clobber stack data
 +      }
 +      if debug.efence != 0 || stackFromSystem != 0 {
 +              if debug.efence != 0 || stackFaultOnFree != 0 {
 +                      sysFault(v, n)
 +              } else {
 +                      sysFree(v, n, &memstats.stacks_sys)
 +              }
 +              return
 +      }
 +      if stackCache != 0 && n < _FixedStack<<_NumStackOrders && n < _StackCacheSize {
 +              order := uint8(0)
 +              n2 := n
 +              for n2 > _FixedStack {
 +                      order++
 +                      n2 >>= 1
 +              }
 +              x := (*mlink)(v)
 +              c := gp.m.mcache
 +              if c == nil || gp.m.gcing != 0 || gp.m.helpgc != 0 {
 +                      lock(&stackpoolmu)
 +                      stackpoolfree(x, order)
 +                      unlock(&stackpoolmu)
 +              } else {
 +                      if c.stackcache[order].size >= _StackCacheSize {
 +                              stackcacherelease(c, order)
 +                      }
 +                      x.next = c.stackcache[order].list
 +                      c.stackcache[order].list = x
 +                      c.stackcache[order].size += n
 +              }
 +      } else {
 +              s := mHeap_Lookup(&mheap_, v)
 +              if s.state != _MSpanStack {
 +                      println(hex(s.start<<_PageShift), v)
 +                      gothrow("bad span state")
 +              }
 +              mHeap_FreeStack(&mheap_, s)
 +      }
 +}
 +
 +var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real
 +
 +var mapnames = []string{
 +      _BitsDead:    "---",
 +      _BitsScalar:  "scalar",
 +      _BitsPointer: "ptr",
 +}
 +
 +// Stack frame layout
 +//
 +// (x86)
 +// +------------------+
 +// | args from caller |
 +// +------------------+ <- frame->argp
 +// |  return address  |
 +// +------------------+ <- frame->varp
 +// |     locals       |
 +// +------------------+
 +// |  args to callee  |
 +// +------------------+ <- frame->sp
 +//
 +// (arm)
 +// +------------------+
 +// | args from caller |
 +// +------------------+ <- frame->argp
 +// | caller's retaddr |
 +// +------------------+ <- frame->varp
 +// |     locals       |
 +// +------------------+
 +// |  args to callee  |
 +// +------------------+
 +// |  return address  |
 +// +------------------+ <- frame->sp
 +
 +type adjustinfo struct {
 +      old   stack
 +      delta uintptr // ptr distance from old to new stack (newbase - oldbase)
 +}
 +
 +// Adjustpointer checks whether *vpp is in the old stack described by adjinfo.
 +// If so, it rewrites *vpp to point into the new stack.
 +func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) {
 +      pp := (*unsafe.Pointer)(vpp)
 +      p := *pp
 +      if stackDebug >= 4 {
 +              print("        ", pp, ":", p, "\n")
 +      }
 +      if adjinfo.old.lo <= uintptr(p) && uintptr(p) < adjinfo.old.hi {
 +              *pp = add(p, adjinfo.delta)
 +              if stackDebug >= 3 {
 +                      print("        adjust ptr ", pp, ":", p, " -> ", *pp, "\n")
 +              }
 +      }
 +}
 +
 +type gobitvector struct {
 +      n        uintptr
 +      bytedata []uint8
 +}
 +
 +func gobv(bv bitvector) gobitvector {
 +      return gobitvector{
 +              uintptr(bv.n),
 +              (*[1 << 30]byte)(unsafe.Pointer(bv.bytedata))[:(bv.n+7)/8],
 +      }
 +}
 +
 +func ptrbits(bv *gobitvector, i uintptr) uint8 {
 +      return (bv.bytedata[i/4] >> ((i & 3) * 2)) & 3
 +}
 +
 +// bv describes the memory starting at address scanp.
 +// Adjust any pointers contained therein.
 +func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f *_func) {
 +      bv := gobv(*cbv)
 +      minp := adjinfo.old.lo
 +      maxp := adjinfo.old.hi
 +      delta := adjinfo.delta
 +      num := uintptr(bv.n / _BitsPerPointer)
 +      for i := uintptr(0); i < num; i++ {
 +              if stackDebug >= 4 {
 +                      print("        ", add(scanp, i*ptrSize), ":", mapnames[ptrbits(&bv, i)], ":", hex(*(*uintptr)(add(scanp, i*ptrSize))), " # ", i, " ", bv.bytedata[i/4], "\n")
 +              }
 +              switch ptrbits(&bv, i) {
 +              default:
 +                      gothrow("unexpected pointer bits")
 +              case _BitsDead:
 +                      if debug.gcdead != 0 {
 +                              *(*unsafe.Pointer)(add(scanp, i*ptrSize)) = unsafe.Pointer(uintptr(poisonStack))
 +                      }
 +              case _BitsScalar:
 +                      // ok
 +              case _BitsPointer:
 +                      p := *(*unsafe.Pointer)(add(scanp, i*ptrSize))
 +                      up := uintptr(p)
 +                      if f != nil && 0 < up && up < _PageSize && invalidptr != 0 || up == poisonGC || up == poisonStack {
 +                              // Looks like a junk value in a pointer slot.
 +                              // Live analysis wrong?
 +                              getg().m.traceback = 2
 +                              print("runtime: bad pointer in frame ", gofuncname(f), " at ", add(scanp, i*ptrSize), ": ", p, "\n")
 +                              gothrow("invalid stack pointer")
 +                      }
 +                      if minp <= up && up < maxp {
 +                              if stackDebug >= 3 {
 +                                      print("adjust ptr ", p, " ", gofuncname(f), "\n")
 +                              }
 +                              *(*unsafe.Pointer)(add(scanp, i*ptrSize)) = unsafe.Pointer(up + delta)
 +                      }
 +              }
 +      }
 +}
 +
 +// Note: the argument/return area is adjusted by the callee.
 +func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
 +      adjinfo := (*adjustinfo)(arg)
 +      targetpc := frame.continpc
 +      if targetpc == 0 {
 +              // Frame is dead.
 +              return true
 +      }
 +      f := frame.fn
 +      if stackDebug >= 2 {
 +              print("    adjusting ", funcname(f), " frame=[", hex(frame.sp), ",", hex(frame.fp), "] pc=", hex(frame.pc), " continpc=", hex(frame.continpc), "\n")
 +      }
 +      if f.entry == systemstack_switchPC {
 +              // A special routine at the bottom of stack of a goroutine that does an systemstack call.
 +              // We will allow it to be copied even though we don't
 +              // have full GC info for it (because it is written in asm).
 +              return true
 +      }
 +      if targetpc != f.entry {
 +              targetpc--
 +      }
 +      pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
 +      if pcdata == -1 {
 +              pcdata = 0 // in prologue
 +      }
 +
 +      // Adjust local variables if stack frame has been allocated.
 +      size := frame.varp - frame.sp
 +      var minsize uintptr
 +      if thechar != '6' && thechar != '8' {
 +              minsize = ptrSize
 +      } else {
 +              minsize = 0
 +      }
 +      if size > minsize {
 +              var bv bitvector
 +              stackmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
 +              if stackmap == nil || stackmap.n <= 0 {
 +                      print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
 +                      gothrow("missing stackmap")
 +              }
 +              // Locals bitmap information, scan just the pointers in locals.
 +              if pcdata < 0 || pcdata >= stackmap.n {
 +                      // don't know where we are
 +                      print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n")
 +                      gothrow("bad symbol table")
 +              }
 +              bv = stackmapdata(stackmap, pcdata)
 +              size = (uintptr(bv.n) * ptrSize) / _BitsPerPointer
 +              if stackDebug >= 3 {
 +                      print("      locals ", pcdata, "/", stackmap.n, " ", size/ptrSize, " words ", bv.bytedata, "\n")
 +              }
 +              adjustpointers(unsafe.Pointer(frame.varp-size), &bv, adjinfo, f)
 +      }
 +
 +      // Adjust arguments.
 +      if frame.arglen > 0 {
 +              var bv bitvector
 +              if frame.argmap != nil {
 +                      bv = *frame.argmap
 +              } else {
 +                      stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
 +                      if stackmap == nil || stackmap.n <= 0 {
 +                              print("runtime: frame ", funcname(f), " untyped args ", frame.argp, "+", uintptr(frame.arglen), "\n")
 +                              gothrow("missing stackmap")
 +                      }
 +                      if pcdata < 0 || pcdata >= stackmap.n {
 +                              // don't know where we are
 +                              print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n")
 +                              gothrow("bad symbol table")
 +                      }
 +                      bv = stackmapdata(stackmap, pcdata)
 +              }
 +              if stackDebug >= 3 {
 +                      print("      args\n")
 +              }
 +              adjustpointers(unsafe.Pointer(frame.argp), &bv, adjinfo, nil)
 +      }
 +      return true
 +}
 +
 +func adjustctxt(gp *g, adjinfo *adjustinfo) {
 +      adjustpointer(adjinfo, (unsafe.Pointer)(&gp.sched.ctxt))
 +}
 +
 +func adjustdefers(gp *g, adjinfo *adjustinfo) {
 +      // Adjust defer argument blocks the same way we adjust active stack frames.
 +      tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo)))
 +
 +      // Adjust pointers in the Defer structs.
 +      // Defer structs themselves are never on the stack.
 +      for d := gp._defer; d != nil; d = d.link {
 +              adjustpointer(adjinfo, (unsafe.Pointer)(&d.fn))
 +              adjustpointer(adjinfo, (unsafe.Pointer)(&d.argp))
 +              adjustpointer(adjinfo, (unsafe.Pointer)(&d._panic))
 +      }
 +}
 +
 +func adjustpanics(gp *g, adjinfo *adjustinfo) {
 +      // Panics are on stack and already adjusted.
 +      // Update pointer to head of list in G.
 +      adjustpointer(adjinfo, (unsafe.Pointer)(&gp._panic))
 +}
 +
 +func adjustsudogs(gp *g, adjinfo *adjustinfo) {
 +      // the data elements pointed to by a SudoG structure
 +      // might be in the stack.
 +      for s := gp.waiting; s != nil; s = s.waitlink {
 +              adjustpointer(adjinfo, (unsafe.Pointer)(&s.elem))
 +              adjustpointer(adjinfo, (unsafe.Pointer)(&s.selectdone))
 +      }
 +}
 +
 +func fillstack(stk stack, b byte) {
 +      for p := stk.lo; p < stk.hi; p++ {
 +              *(*byte)(unsafe.Pointer(p)) = b
 +      }
 +}
 +
 +// Copies gp's stack to a new stack of a different size.
 +func copystack(gp *g, newsize uintptr) {
 +      if gp.syscallsp != 0 {
 +              gothrow("stack growth not allowed in system call")
 +      }
 +      old := gp.stack
 +      if old.lo == 0 {
 +              gothrow("nil stackbase")
 +      }
 +      used := old.hi - gp.sched.sp
 +
 +      // allocate new stack
 +      new := stackalloc(uint32(newsize))
 +      if stackPoisonCopy != 0 {
 +              fillstack(new, 0xfd)
 +      }
 +      if stackDebug >= 1 {
 +              print("copystack gp=", gp, " [", hex(old.lo), " ", hex(old.hi-used), " ", hex(old.hi), "]/", old.hi-old.lo, " -> [", hex(new.lo), " ", hex(new.hi-used), " ", hex(new.hi), "]/", newsize, "\n")
 +      }
 +
 +      // adjust pointers in the to-be-copied frames
 +      var adjinfo adjustinfo
 +      adjinfo.old = old
 +      adjinfo.delta = new.hi - old.hi
 +      gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, adjustframe, noescape(unsafe.Pointer(&adjinfo)), 0)
 +
 +      // adjust other miscellaneous things that have pointers into stacks.
 +      adjustctxt(gp, &adjinfo)
 +      adjustdefers(gp, &adjinfo)
 +      adjustpanics(gp, &adjinfo)
 +      adjustsudogs(gp, &adjinfo)
 +
 +      // copy the stack to the new location
 +      if stackPoisonCopy != 0 {
 +              fillstack(new, 0xfb)
 +      }
 +      memmove(unsafe.Pointer(new.hi-used), unsafe.Pointer(old.hi-used), used)
 +
++      oldstatus := casgcopystack(gp) // cas from Gwaiting or Grunnable to Gcopystack, return old status
 +
 +      // Swap out old stack for new one
 +      gp.stack = new
 +      gp.stackguard0 = new.lo + _StackGuard // NOTE: might clobber a preempt request
 +      gp.sched.sp = new.hi - used
 +
 +      casgstatus(gp, _Gcopystack, oldstatus) // oldstatus is Gwaiting or Grunnable
 +
 +      // free old stack
 +      if stackPoisonCopy != 0 {
 +              fillstack(old, 0xfc)
 +      }
 +      if newsize > old.hi-old.lo {
 +              // growing, free stack immediately
 +              stackfree(old)
 +      } else {
 +              // shrinking, queue up free operation.  We can't actually free the stack
 +              // just yet because we might run into the following situation:
 +              // 1) GC starts, scans a SudoG but does not yet mark the SudoG.elem pointer
 +              // 2) The stack that pointer points to is shrunk
 +              // 3) The old stack is freed
 +              // 4) The containing span is marked free
 +              // 5) GC attempts to mark the SudoG.elem pointer.  The marking fails because
 +              //    the pointer looks like a pointer into a free span.
 +              // By not freeing, we prevent step #4 until GC is done.
 +              lock(&stackpoolmu)
 +              *(*stack)(unsafe.Pointer(old.lo)) = stackfreequeue
 +              stackfreequeue = old
 +              unlock(&stackpoolmu)
 +      }
 +}
 +
 +// round x up to a power of 2.
 +func round2(x int32) int32 {
 +      s := uint(0)
 +      for 1<<s < x {
 +              s++
 +      }
 +      return 1 << s
 +}
 +
 +// Called from runtime·morestack when more stack is needed.
 +// Allocate larger stack and relocate to new stack.
 +// Stack growth is multiplicative, for constant amortized cost.
 +//
 +// g->atomicstatus will be Grunning or Gscanrunning upon entry.
 +// If the GC is trying to stop this g then it will set preemptscan to true.
 +func newstack() {
 +      thisg := getg()
 +      // TODO: double check all gp. shouldn't be getg().
 +      if thisg.m.morebuf.g.stackguard0 == stackFork {
 +              gothrow("stack growth after fork")
 +      }
 +      if thisg.m.morebuf.g != thisg.m.curg {
 +              print("runtime: newstack called from g=", thisg.m.morebuf.g, "\n"+"\tm=", thisg.m, " m->curg=", thisg.m.curg, " m->g0=", thisg.m.g0, " m->gsignal=", thisg.m.gsignal, "\n")
 +              morebuf := thisg.m.morebuf
 +              traceback(morebuf.pc, morebuf.sp, morebuf.lr, morebuf.g)
 +              gothrow("runtime: wrong goroutine in newstack")
 +      }
 +      if thisg.m.curg.throwsplit {
 +              gp := thisg.m.curg
 +              // Update syscallsp, syscallpc in case traceback uses them.
 +              morebuf := thisg.m.morebuf
 +              gp.syscallsp = morebuf.sp
 +              gp.syscallpc = morebuf.pc
 +              print("runtime: newstack sp=", hex(gp.sched.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")
 +              gothrow("runtime: stack split at bad time")
 +      }
 +
 +      // The goroutine must be executing in order to call newstack,
 +      // so it must be Grunning or Gscanrunning.
 +
 +      gp := thisg.m.curg
 +      morebuf := thisg.m.morebuf
 +      thisg.m.morebuf.pc = 0
 +      thisg.m.morebuf.lr = 0
 +      thisg.m.morebuf.sp = 0
 +      thisg.m.morebuf.g = nil
 +
 +      casgstatus(gp, _Grunning, _Gwaiting)
 +      gp.waitreason = "stack growth"
 +
 +      rewindmorestack(&gp.sched)
 +
 +      if gp.stack.lo == 0 {
 +              gothrow("missing stack in newstack")
 +      }
 +      sp := gp.sched.sp
 +      if thechar == '6' || thechar == '8' {
 +              // The call to morestack cost a word.
 +              sp -= ptrSize
 +      }
 +      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")
 +              gothrow("runtime: split stack overflow")
 +      }
 +
 +      if gp.stackguard0 == stackPreempt {
 +              if gp == thisg.m.g0 {
 +                      gothrow("runtime: preempt g0")
 +              }
 +              if thisg.m.p == nil && thisg.m.locks == 0 {
 +                      gothrow("runtime: g is running but p is not")
 +              }
 +              if gp.preemptscan {
 +                      gcphasework(gp)
 +                      casgstatus(gp, _Gwaiting, _Grunning)
 +                      gp.stackguard0 = gp.stack.lo + _StackGuard
 +                      gp.preempt = false
 +                      gp.preemptscan = false // Tells the GC premption was successful.
 +                      gogo(&gp.sched)        // never return
 +              }
 +
 +              // Be conservative about where we preempt.
 +              // We are interested in preempting user Go code, not runtime code.
 +              if thisg.m.locks != 0 || thisg.m.mallocing != 0 || thisg.m.gcing != 0 || thisg.m.p.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
 +                      casgstatus(gp, _Gwaiting, _Grunning)
 +                      gogo(&gp.sched) // never return
 +              }
 +
 +              // Act like goroutine called runtime.Gosched.
 +              casgstatus(gp, _Gwaiting, _Grunning)
 +              gosched_m(gp) // never return
 +      }
 +
 +      // Allocate a bigger segment and move the stack.
 +      oldsize := int(gp.stack.hi - gp.stack.lo)
 +      newsize := oldsize * 2
 +      if uintptr(newsize) > maxstacksize {
 +              print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n")
 +              gothrow("stack overflow")
 +      }
 +
 +      // Note that the concurrent GC might be scanning the stack as we try to replace it.
 +      // copystack takes care of the appropriate coordination with the stack scanner.
 +      copystack(gp, uintptr(newsize))
 +      if stackDebug >= 1 {
 +              print("stack grow done\n")
 +      }
 +      casgstatus(gp, _Gwaiting, _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 {
 +              fn = (unsafe.Pointer)(fv.fn)
 +      } 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.
 +func shrinkstack(gp *g) {
 +      if readgstatus(gp) == _Gdead {
 +              if gp.stack.lo != 0 {
 +                      // Free whole stack - it will get reallocated
 +                      // if G is used again.
 +                      stackfree(gp.stack)
 +                      gp.stack.lo = 0
 +                      gp.stack.hi = 0
 +              }
 +              return
 +      }
 +      if gp.stack.lo == 0 {
 +              gothrow("missing stack in shrinkstack")
 +      }
 +
 +      oldsize := gp.stack.hi - gp.stack.lo
 +      newsize := oldsize / 2
 +      if newsize < _FixedStack {
 +              return // don't shrink below the minimum-sized stack
 +      }
 +      used := gp.stack.hi - gp.sched.sp
 +      if used >= oldsize/4 {
 +              return // still using at least 1/4 of the segment.
 +      }
 +
 +      // 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
 +      }
 +
 +      /* TODO
 +      if goos_windows && gp.m != nil && gp.m.libcallsp != 0 {
 +              return
 +      }
 +      */
 +
 +      if stackDebug > 0 {
 +              print("shrinking stack ", oldsize, "->", newsize, "\n")
 +      }
 +      copystack(gp, newsize)
 +}
 +
 +// Do any delayed stack freeing that was queued up during GC.
 +func shrinkfinish() {
 +      lock(&stackpoolmu)
 +      s := stackfreequeue
 +      stackfreequeue = stack{}
 +      unlock(&stackpoolmu)
 +      for s.lo != 0 {
 +              t := *(*stack)(unsafe.Pointer(s.lo))
 +              stackfree(s)
 +              s = t
 +      }
 +}
 +
 +//go:nosplit
 +func morestackc() {
 +      systemstack(func() {
 +              gothrow("attempt to execute C code on Go stack")
 +      })
 +}