| // Copyright 2014 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package runtime | |
| import "unsafe" | |
| //go:linkname runtime_init runtime.init | |
| func runtime_init() | |
| //go:linkname main_init main.init | |
| func main_init() | |
| // main_init_done is a signal used by cgocallbackg that initialization | |
| // has been completed. It is made before _cgo_notify_runtime_init_done, | |
| // so all cgo calls can rely on it existing. When main_init is complete, | |
| // it is closed, meaning cgocallbackg can reliably receive from it. | |
| var main_init_done chan bool | |
| //go:linkname main_main main.main | |
| func main_main() | |
| // runtimeInitTime is the nanotime() at which the runtime started. | |
| var runtimeInitTime int64 | |
| // The main goroutine. | |
| func main() { | |
| g := getg() | |
| // Racectx of m0->g0 is used only as the parent of the main goroutine. | |
| // It must not be used for anything else. | |
| g.m.g0.racectx = 0 | |
| // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit. | |
| // Using decimal instead of binary GB and MB because | |
| // they look nicer in the stack overflow failure message. | |
| if ptrSize == 8 { | |
| maxstacksize = 1000000000 | |
| } else { | |
| maxstacksize = 250000000 | |
| } | |
| // Record when the world started. | |
| runtimeInitTime = nanotime() | |
| systemstack(func() { | |
| newm(sysmon, nil) | |
| }) | |
| // Lock the main goroutine onto this, the main OS thread, | |
| // during initialization. Most programs won't care, but a few | |
| // do require certain calls to be made by the main thread. | |
| // Those can arrange for main.main to run in the main thread | |
| // by calling runtime.LockOSThread during initialization | |
| // to preserve the lock. | |
| lockOSThread() | |
| if g.m != &m0 { | |
| throw("runtime.main not on m0") | |
| } | |
| runtime_init() // must be before defer | |
| // Defer unlock so that runtime.Goexit during init does the unlock too. | |
| needUnlock := true | |
| defer func() { | |
| if needUnlock { | |
| unlockOSThread() | |
| } | |
| }() | |
| gcenable() | |
| main_init_done = make(chan bool) | |
| if iscgo { | |
| if _cgo_thread_start == nil { | |
| throw("_cgo_thread_start missing") | |
| } | |
| if _cgo_malloc == nil { | |
| throw("_cgo_malloc missing") | |
| } | |
| if _cgo_free == nil { | |
| throw("_cgo_free missing") | |
| } | |
| if GOOS != "windows" { | |
| if _cgo_setenv == nil { | |
| throw("_cgo_setenv missing") | |
| } | |
| if _cgo_unsetenv == nil { | |
| throw("_cgo_unsetenv missing") | |
| } | |
| } | |
| if _cgo_notify_runtime_init_done == nil { | |
| throw("_cgo_notify_runtime_init_done missing") | |
| } | |
| cgocall(_cgo_notify_runtime_init_done, nil) | |
| } | |
| main_init() | |
| close(main_init_done) | |
| needUnlock = false | |
| unlockOSThread() | |
| if isarchive || islibrary { | |
| // A program compiled with -buildmode=c-archive or c-shared | |
| // has a main, but it is not executed. | |
| return | |
| } | |
| main_main() | |
| if raceenabled { | |
| racefini() | |
| } | |
| // Make racy client program work: if panicking on | |
| // another goroutine at the same time as main returns, | |
| // let the other goroutine finish printing the panic trace. | |
| // Once it does, it will exit. See issue 3934. | |
| if panicking != 0 { | |
| gopark(nil, nil, "panicwait", traceEvGoStop, 1) | |
| } | |
| exit(0) | |
| for { | |
| var x *int32 | |
| *x = 0 | |
| } | |
| } | |
| // os_beforeExit is called from os.Exit(0). | |
| //go:linkname os_beforeExit os.runtime_beforeExit | |
| func os_beforeExit() { | |
| if raceenabled { | |
| racefini() | |
| } | |
| } | |
| // start forcegc helper goroutine | |
| func init() { | |
| go forcegchelper() | |
| } | |
| func forcegchelper() { | |
| forcegc.g = getg() | |
| for { | |
| lock(&forcegc.lock) | |
| if forcegc.idle != 0 { | |
| throw("forcegc: phase error") | |
| } | |
| atomicstore(&forcegc.idle, 1) | |
| goparkunlock(&forcegc.lock, "force gc (idle)", traceEvGoBlock, 1) | |
| // this goroutine is explicitly resumed by sysmon | |
| if debug.gctrace > 0 { | |
| println("GC forced") | |
| } | |
| startGC(gcBackgroundMode, true) | |
| } | |
| } | |
| //go:nosplit | |
| // Gosched yields the processor, allowing other goroutines to run. It does not | |
| // suspend the current goroutine, so execution resumes automatically. | |
| func Gosched() { | |
| mcall(gosched_m) | |
| } | |
| // Puts the current goroutine into a waiting state and calls unlockf. | |
| // If unlockf returns false, the goroutine is resumed. | |
| func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string, traceEv byte, traceskip int) { | |
| mp := acquirem() | |
| gp := mp.curg | |
| status := readgstatus(gp) | |
| if status != _Grunning && status != _Gscanrunning { | |
| throw("gopark: bad g status") | |
| } | |
| mp.waitlock = lock | |
| mp.waitunlockf = *(*unsafe.Pointer)(unsafe.Pointer(&unlockf)) | |
| gp.waitreason = reason | |
| mp.waittraceev = traceEv | |
| mp.waittraceskip = traceskip | |
| releasem(mp) | |
| // can't do anything that might move the G between Ms here. | |
| mcall(park_m) | |
| } | |
| // Puts the current goroutine into a waiting state and unlocks the lock. | |
| // The goroutine can be made runnable again by calling goready(gp). | |
| func goparkunlock(lock *mutex, reason string, traceEv byte, traceskip int) { | |
| gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceEv, traceskip) | |
| } | |
| func goready(gp *g, traceskip int) { | |
| systemstack(func() { | |
| ready(gp, traceskip) | |
| }) | |
| } | |
| //go:nosplit | |
| func acquireSudog() *sudog { | |
| // Delicate dance: the semaphore implementation calls | |
| // acquireSudog, acquireSudog calls new(sudog), | |
| // new calls malloc, malloc can call the garbage collector, | |
| // and the garbage collector calls the semaphore implementation | |
| // in stopTheWorld. | |
| // Break the cycle by doing acquirem/releasem around new(sudog). | |
| // The acquirem/releasem increments m.locks during new(sudog), | |
| // which keeps the garbage collector from being invoked. | |
| mp := acquirem() | |
| pp := mp.p.ptr() | |
| if len(pp.sudogcache) == 0 { | |
| lock(&sched.sudoglock) | |
| // First, try to grab a batch from central cache. | |
| for len(pp.sudogcache) < cap(pp.sudogcache)/2 && sched.sudogcache != nil { | |
| s := sched.sudogcache | |
| sched.sudogcache = s.next | |
| s.next = nil | |
| pp.sudogcache = append(pp.sudogcache, s) | |
| } | |
| unlock(&sched.sudoglock) | |
| // If the central cache is empty, allocate a new one. | |
| if len(pp.sudogcache) == 0 { | |
| pp.sudogcache = append(pp.sudogcache, new(sudog)) | |
| } | |
| } | |
| n := len(pp.sudogcache) | |
| s := pp.sudogcache[n-1] | |
| pp.sudogcache[n-1] = nil | |
| pp.sudogcache = pp.sudogcache[:n-1] | |
| if s.elem != nil { | |
| throw("acquireSudog: found s.elem != nil in cache") | |
| } | |
| releasem(mp) | |
| return s | |
| } | |
| //go:nosplit | |
| func releaseSudog(s *sudog) { | |
| if s.elem != nil { | |
| throw("runtime: sudog with non-nil elem") | |
| } | |
| if s.selectdone != nil { | |
| throw("runtime: sudog with non-nil selectdone") | |
| } | |
| if s.next != nil { | |
| throw("runtime: sudog with non-nil next") | |
| } | |
| if s.prev != nil { | |
| throw("runtime: sudog with non-nil prev") | |
| } | |
| if s.waitlink != nil { | |
| throw("runtime: sudog with non-nil waitlink") | |
| } | |
| gp := getg() | |
| if gp.param != nil { | |
| throw("runtime: releaseSudog with non-nil gp.param") | |
| } | |
| mp := acquirem() // avoid rescheduling to another P | |
| pp := mp.p.ptr() | |
| if len(pp.sudogcache) == cap(pp.sudogcache) { | |
| // Transfer half of local cache to the central cache. | |
| var first, last *sudog | |
| for len(pp.sudogcache) > cap(pp.sudogcache)/2 { | |
| n := len(pp.sudogcache) | |
| p := pp.sudogcache[n-1] | |
| pp.sudogcache[n-1] = nil | |
| pp.sudogcache = pp.sudogcache[:n-1] | |
| if first == nil { | |
| first = p | |
| } else { | |
| last.next = p | |
| } | |
| last = p | |
| } | |
| lock(&sched.sudoglock) | |
| last.next = sched.sudogcache | |
| sched.sudogcache = first | |
| unlock(&sched.sudoglock) | |
| } | |
| pp.sudogcache = append(pp.sudogcache, s) | |
| releasem(mp) | |
| } | |
| // funcPC returns the entry PC of the function f. | |
| // It assumes that f is a func value. Otherwise the behavior is undefined. | |
| //go:nosplit | |
| func funcPC(f interface{}) uintptr { | |
| return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize)) | |
| } | |
| // called from assembly | |
| func badmcall(fn func(*g)) { | |
| throw("runtime: mcall called on m->g0 stack") | |
| } | |
| func badmcall2(fn func(*g)) { | |
| throw("runtime: mcall function returned") | |
| } | |
| func badreflectcall() { | |
| panic("runtime: arg size to reflect.call more than 1GB") | |
| } | |
| func lockedOSThread() bool { | |
| gp := getg() | |
| return gp.lockedm != nil && gp.m.lockedg != nil | |
| } | |
| var ( | |
| allgs []*g | |
| allglock mutex | |
| ) | |
| func allgadd(gp *g) { | |
| if readgstatus(gp) == _Gidle { | |
| throw("allgadd: bad status Gidle") | |
| } | |
| lock(&allglock) | |
| allgs = append(allgs, gp) | |
| allg = &allgs[0] | |
| allglen = uintptr(len(allgs)) | |
| unlock(&allglock) | |
| } |