// Called to receive the next queued signal. // Must only be called from a single goroutine at a time. //go:linkname signal_recv os/signal.signal_recv func signal_recv() uint32 { for { // Serve any signals from local copy. for i := uint32(0); i < _NSIG; i++ { if sig.recv[i/32]&(1<<(i&31)) != 0 { sig.recv[i/32] &^= 1 << (i & 31) return i } } // Wait for updates to be available from signal sender. Receive: for { switch atomic.Load(&sig.state) { default: throw("signal_recv: inconsistent state") case sigIdle: if atomic.Cas(&sig.state, sigIdle, sigReceiving) { notetsleepg(&sig.note, -1) noteclear(&sig.note) break Receive } case sigSending: if atomic.Cas(&sig.state, sigSending, sigIdle) { break Receive } } } // Incorporate updates from sender into local copy. for i := range sig.mask { sig.recv[i] = atomic.Xchg(&sig.mask[i], 0) } } }
func lock(l *mutex) { gp := getg() if gp.m.locks < 0 { throw("runtime·lock: lock count") } gp.m.locks++ // Speculative grab for lock. v := atomic.Xchg(key32(&l.key), mutex_locked) if v == mutex_unlocked { return } // wait is either MUTEX_LOCKED or MUTEX_SLEEPING // depending on whether there is a thread sleeping // on this mutex. If we ever change l->key from // MUTEX_SLEEPING to some other value, we must be // careful to change it back to MUTEX_SLEEPING before // returning, to ensure that the sleeping thread gets // its wakeup call. wait := v // On uniprocessors, no point spinning. // On multiprocessors, spin for ACTIVE_SPIN attempts. spin := 0 if ncpu > 1 { spin = active_spin } for { // Try for lock, spinning. for i := 0; i < spin; i++ { for l.key == mutex_unlocked { if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { return } } procyield(active_spin_cnt) } // Try for lock, rescheduling. for i := 0; i < passive_spin; i++ { for l.key == mutex_unlocked { if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { return } } osyield() } // Sleep. v = atomic.Xchg(key32(&l.key), mutex_sleeping) if v == mutex_unlocked { return } wait = mutex_sleeping futexsleep(key32(&l.key), mutex_sleeping, -1) } }
// sweeps one span // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep //go:nowritebarrier func sweepone() uintptr { _g_ := getg() // increment locks to ensure that the goroutine is not preempted // in the middle of sweep thus leaving the span in an inconsistent state for next GC _g_.m.locks++ sg := mheap_.sweepgen for { idx := atomic.Xadd(&sweep.spanidx, 1) - 1 if idx >= uint32(len(work.spans)) { mheap_.sweepdone = 1 _g_.m.locks-- return ^uintptr(0) } s := work.spans[idx] if s.state != mSpanInUse { s.sweepgen = sg continue } if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) { continue } npages := s.npages if !s.sweep(false) { npages = 0 } _g_.m.locks-- return npages } }
// Sweeps spans in list until reclaims at least npages into heap. // Returns the actual number of pages reclaimed. func (h *mheap) reclaimList(list *mSpanList, npages uintptr) uintptr { n := uintptr(0) sg := mheap_.sweepgen retry: for s := list.first; s != nil; s = s.next { if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { list.remove(s) // swept spans are at the end of the list list.insertBack(s) unlock(&h.lock) snpages := s.npages if s.sweep(false) { n += snpages } lock(&h.lock) if n >= npages { return n } // the span could have been moved elsewhere goto retry } if s.sweepgen == sg-1 { // the span is being sweept by background sweeper, skip continue } // already swept empty span, // all subsequent ones must also be either swept or in process of sweeping break } return n }
func (q *waitq) dequeue() *sudog { for { sgp := q.first if sgp == nil { return nil } y := sgp.next if y == nil { q.first = nil q.last = nil } else { y.prev = nil q.first = y sgp.next = nil // mark as removed (see dequeueSudog) } // if sgp participates in a select and is already signaled, ignore it if sgp.selectdone != nil { // claim the right to signal if *sgp.selectdone != 0 || !atomic.Cas(sgp.selectdone, 0, 1) { continue } } return sgp } }
//go:nosplit func semasleep(ns int64) int32 { _g_ := getg() // Compute sleep deadline. var tsp *timespec if ns >= 0 { var ts timespec var nsec int32 ns += nanotime() ts.set_sec(timediv(ns, 1000000000, &nsec)) ts.set_nsec(nsec) tsp = &ts } for { v := atomic.Load(&_g_.m.waitsemacount) if v > 0 { if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { return 0 // semaphore acquired } continue } // Sleep until unparked by semawakeup or timeout. ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil) if ret == _ETIMEDOUT { return -1 } } }
// Dequeue the `sudog` from the waiters linked list func (q *waitq) dequeue() *sudog { for { sgp := q.first if sgp == nil { return nil } y := sgp.next if y == nil { q.first = nil q.last = nil } else { y.prev = nil q.first = y sgp.next = nil // mark as removed (see dequeueSudog) } if sgp.selectdone != nil { if *sgp.selectdone != 0 || !atomic.Cas(sgp.selectdone, 0, 1) { continue } } return sgp } }
// sweeps one span // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep //go:nowritebarrier func sweepone() uintptr { _g_ := getg() // increment locks to ensure that the goroutine is not preempted // in the middle of sweep thus leaving the span in an inconsistent state for next GC _g_.m.locks++ sg := mheap_.sweepgen for { idx := atomic.Xadd(&sweep.spanidx, 1) - 1 if idx >= uint32(len(work.spans)) { mheap_.sweepdone = 1 _g_.m.locks-- if debug.gcpacertrace > 0 && idx == uint32(len(work.spans)) { print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", mheap_.spanBytesAlloc>>20, "MB of spans; swept ", mheap_.pagesSwept, " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n") } return ^uintptr(0) } s := work.spans[idx] if s.state != mSpanInUse { s.sweepgen = sg continue } if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) { continue } npages := s.npages if !s.sweep(false) { npages = 0 } _g_.m.locks-- return npages } }
// gcLockStackBarriers synchronizes with tracebacks of gp's stack // during sigprof for installation or removal of stack barriers. It // blocks until any current sigprof is done tracebacking gp's stack // and then disallows profiling tracebacks of gp's stack. // // This is necessary because a sigprof during barrier installation or // removal could observe inconsistencies between the stkbar array and // the stack itself and crash. // //go:nosplit func gcLockStackBarriers(gp *g) { // Disable preemption so scanstack cannot run while the caller // is manipulating the stack barriers. acquirem() for !atomic.Cas(&gp.stackLock, 0, 1) { osyield() } }
//go:nosplit func gcTryLockStackBarriers(gp *g) bool { mp := acquirem() result := atomic.Cas(&gp.stackLock, 0, 1) if !result { releasem(mp) } return result }
func cansemacquire(addr *uint32) bool { for { v := atomic.Load(addr) if v == 0 { return false } if atomic.Cas(addr, v, v-1) { return true } } }
// 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("runtime: casgstatus: oldval=", hex(oldval), " newval=", hex(newval), "\n") throw("casgstatus: bad incoming values") }) } if oldval == _Grunning && gp.gcscanvalid { // If oldvall == _Grunning, then the actual status must be // _Grunning or _Grunning|_Gscan; either way, // we own gp.gcscanvalid, so it's safe to read. // gp.gcscanvalid must not be true when we are running. print("runtime: casgstatus ", hex(oldval), "->", hex(newval), " gp.status=", hex(gp.atomicstatus), " gp.gcscanvalid=true\n") throw("casgstatus") } // See http://golang.org/cl/21503 for justification of the yield delay. const yieldDelay = 5 * 1000 var nextYield int64 // loop if gp->atomicstatus is in a scan state giving // GC time to finish and change the state to oldval. for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ { if oldval == _Gwaiting && gp.atomicstatus == _Grunnable { systemstack(func() { throw("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) // }) // } // But meanwhile just yield. if i == 0 { nextYield = nanotime() + yieldDelay } if nanotime() < nextYield { for x := 0; x < 10 && gp.atomicstatus != oldval; x++ { procyield(1) } } else { osyield() nextYield = nanotime() + yieldDelay/2 } } if newval == _Grunning && gp.gcscanvalid { // Run queueRescan on the system stack so it has more space. systemstack(func() { queueRescan(gp) }) } }
// sweeps one span // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep //go:nowritebarrier func sweepone() uintptr { _g_ := getg() // increment locks to ensure that the goroutine is not preempted // in the middle of sweep thus leaving the span in an inconsistent state for next GC _g_.m.locks++ sg := mheap_.sweepgen for { s := mheap_.sweepSpans[1-sg/2%2].pop() if s == nil { mheap_.sweepdone = 1 _g_.m.locks-- if debug.gcpacertrace > 0 && atomic.Cas(&sweep.pacertracegen, sg-2, sg) { print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", mheap_.spanBytesAlloc>>20, "MB of spans; swept ", mheap_.pagesSwept, " pages at ", mheap_.sweepPagesPerByte, " pages/byte\n") } return ^uintptr(0) } if s.state != mSpanInUse { // This can happen if direct sweeping already // swept this span, but in that case the sweep // generation should always be up-to-date. if s.sweepgen != sg { print("runtime: bad span s.state=", s.state, " s.sweepgen=", s.sweepgen, " sweepgen=", sg, "\n") throw("non in-use span in unswept list") } continue } if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) { continue } npages := s.npages if !s.sweep(false) { // Span is still in-use, so this returned no // pages to the heap and the span needs to // move to the swept in-use list. npages = 0 } _g_.m.locks-- return npages } }
// Called from sighandler to send a signal back out of the signal handling thread. // Reports whether the signal was sent. If not, the caller typically crashes the program. func sigsend(s uint32) bool { bit := uint32(1) << uint(s&31) if !sig.inuse || s >= uint32(32*len(sig.wanted)) || sig.wanted[s/32]&bit == 0 { return false } // Add signal to outgoing queue. for { mask := sig.mask[s/32] if mask&bit != 0 { return true // signal already in queue } if atomic.Cas(&sig.mask[s/32], mask, mask|bit) { break } } // Notify receiver that queue has new bit. Send: for { switch atomic.Load(&sig.state) { default: throw("sigsend: inconsistent state") case sigIdle: if atomic.Cas(&sig.state, sigIdle, sigSending) { break Send } case sigSending: // notification already pending break Send case sigReceiving: if atomic.Cas(&sig.state, sigReceiving, sigIdle) { notewakeup(&sig.note) break Send } } } return true }
func goexitsall(status *byte) { var buf [_ERRMAX]byte if !atomic.Cas(&exiting, 0, 1) { return } getg().m.locks++ n := copy(buf[:], goexits) n = copy(buf[n:], gostringnocopy(status)) pid := getpid() for mp := (*m)(atomic.Loadp(unsafe.Pointer(&allm))); mp != nil; mp = mp.alllink { if mp.procid != 0 && mp.procid != pid { postnote(mp.procid, buf[:]) } } getg().m.locks-- }
// flushlog tries to flush the current log and switch to the other one. // flushlog is called from evict, called from add, called from the signal handler, // so it cannot allocate memory or block. It can try to swap logs with // the writing goroutine, as explained in the comment at the top of this file. //go:nowritebarrierrec func (p *cpuProfile) flushlog() bool { if !atomic.Cas(&p.handoff, 0, uint32(p.nlog)) { return false } notewakeup(&p.wait) p.toggle = 1 - p.toggle log := &p.log[p.toggle] q := 0 if p.lost > 0 { lostPC := funcPC(lostProfileData) log[0] = p.lost log[1] = 1 log[2] = lostPC q = 3 p.lost = 0 } p.nlog = q return true }
// Returns only when span s has been swept. //go:nowritebarrier func (s *mspan) ensureSwept() { // Caller must disable preemption. // Otherwise when this function returns the span can become unswept again // (if GC is triggered on another goroutine). _g_ := getg() if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 { throw("MSpan_EnsureSwept: m is not locked") } sg := mheap_.sweepgen if atomic.Load(&s.sweepgen) == sg { return } // The caller must be sure that the span is a MSpanInUse span. if atomic.Cas(&s.sweepgen, sg-2, sg-1) { s.sweep(false) return } // unfortunate condition, and we don't have efficient means to wait for atomic.Load(&s.sweepgen) != sg { osyield() } }
//go:nosplit func semasleep(ns int64) int32 { _g_ := getg() // Compute sleep deadline. var tsp *timespec if ns >= 0 { var ts timespec var nsec int32 ns += nanotime() ts.set_sec(int64(timediv(ns, 1000000000, &nsec))) ts.set_nsec(nsec) tsp = &ts } for { v := atomic.Load(&_g_.m.waitsemacount) if v > 0 { if atomic.Cas(&_g_.m.waitsemacount, v, v-1) { return 0 // semaphore acquired } continue } // Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0. // // From OpenBSD's __thrsleep(2) manual: // "The abort argument, if not NULL, points to an int that will // be examined [...] immediately before blocking. If that int // is non-zero then __thrsleep() will immediately return EINTR // without blocking." ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount) if ret == _EWOULDBLOCK { return -1 } } }
// Allocate a span to use in an MCache. func (c *mcentral) cacheSpan() *mspan { // Deduct credit for this span allocation and sweep if necessary. deductSweepCredit(uintptr(class_to_size[c.sizeclass]), 0) lock(&c.lock) sg := mheap_.sweepgen retry: var s *mspan for s = c.nonempty.first; s != nil; s = s.next { if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { c.nonempty.remove(s) c.empty.insertBack(s) unlock(&c.lock) s.sweep(true) goto havespan } if s.sweepgen == sg-1 { // the span is being swept by background sweeper, skip continue } // we have a nonempty span that does not require sweeping, allocate from it c.nonempty.remove(s) c.empty.insertBack(s) unlock(&c.lock) goto havespan } for s = c.empty.first; s != nil; s = s.next { if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { // we have an empty span that requires sweeping, // sweep it and see if we can free some space in it c.empty.remove(s) // swept spans are at the end of the list c.empty.insertBack(s) unlock(&c.lock) s.sweep(true) if s.freelist.ptr() != nil { goto havespan } lock(&c.lock) // the span is still empty after sweep // it is already in the empty list, so just retry goto retry } if s.sweepgen == sg-1 { // the span is being swept by background sweeper, skip continue } // already swept empty span, // all subsequent ones must also be either swept or in process of sweeping break } unlock(&c.lock) // Replenish central list if empty. s = c.grow() if s == nil { return nil } lock(&c.lock) c.empty.insertBack(s) unlock(&c.lock) // At this point s is a non-empty span, queued at the end of the empty list, // c is unlocked. havespan: cap := int32((s.npages << _PageShift) / s.elemsize) n := cap - int32(s.ref) if n == 0 { throw("empty span") } usedBytes := uintptr(s.ref) * s.elemsize if usedBytes > 0 { reimburseSweepCredit(usedBytes) } if s.freelist.ptr() == nil { throw("freelist empty") } s.incache = true return s }
func check() { var ( a int8 b uint8 c int16 d uint16 e int32 f uint32 g int64 h uint64 i, i1 float32 j, j1 float64 k, k1 unsafe.Pointer l *uint16 m [4]byte ) type x1t struct { x uint8 } type y1t struct { x1 x1t y uint8 } var x1 x1t var y1 y1t if unsafe.Sizeof(a) != 1 { throw("bad a") } if unsafe.Sizeof(b) != 1 { throw("bad b") } if unsafe.Sizeof(c) != 2 { throw("bad c") } if unsafe.Sizeof(d) != 2 { throw("bad d") } if unsafe.Sizeof(e) != 4 { throw("bad e") } if unsafe.Sizeof(f) != 4 { throw("bad f") } if unsafe.Sizeof(g) != 8 { throw("bad g") } if unsafe.Sizeof(h) != 8 { throw("bad h") } if unsafe.Sizeof(i) != 4 { throw("bad i") } if unsafe.Sizeof(j) != 8 { throw("bad j") } if unsafe.Sizeof(k) != sys.PtrSize { throw("bad k") } if unsafe.Sizeof(l) != sys.PtrSize { throw("bad l") } if unsafe.Sizeof(x1) != 1 { throw("bad unsafe.Sizeof x1") } if unsafe.Offsetof(y1.y) != 1 { throw("bad offsetof y1.y") } if unsafe.Sizeof(y1) != 2 { throw("bad unsafe.Sizeof y1") } if timediv(12345*1000000000+54321, 1000000000, &e) != 12345 || e != 54321 { throw("bad timediv") } var z uint32 z = 1 if !atomic.Cas(&z, 1, 2) { throw("cas1") } if z != 2 { throw("cas2") } z = 4 if atomic.Cas(&z, 5, 6) { throw("cas3") } if z != 4 { throw("cas4") } z = 0xffffffff if !atomic.Cas(&z, 0xffffffff, 0xfffffffe) { throw("cas5") } if z != 0xfffffffe { throw("cas6") } k = unsafe.Pointer(uintptr(0xfedcb123)) if sys.PtrSize == 8 { k = unsafe.Pointer(uintptr(k) << 10) } if casp(&k, nil, nil) { throw("casp1") } k1 = add(k, 1) if !casp(&k, k, k1) { throw("casp2") } if k != k1 { throw("casp3") } m = [4]byte{1, 1, 1, 1} atomic.Or8(&m[1], 0xf0) if m[0] != 1 || m[1] != 0xf1 || m[2] != 1 || m[3] != 1 { throw("atomicor8") } *(*uint64)(unsafe.Pointer(&j)) = ^uint64(0) if j == j { throw("float64nan") } if !(j != j) { throw("float64nan1") } *(*uint64)(unsafe.Pointer(&j1)) = ^uint64(1) if j == j1 { throw("float64nan2") } if !(j != j1) { throw("float64nan3") } *(*uint32)(unsafe.Pointer(&i)) = ^uint32(0) if i == i { throw("float32nan") } if i == i { throw("float32nan1") } *(*uint32)(unsafe.Pointer(&i1)) = ^uint32(1) if i == i1 { throw("float32nan2") } if i == i1 { throw("float32nan3") } testAtomic64() if _FixedStack != round2(_FixedStack) { throw("FixedStack is not power-of-2") } if !checkASM() { throw("assembly checks failed") } }
func check() { // This doesn't currently work for gccgo. Because escape // analysis is not turned on by default, the code below that // takes the address of local variables causes memory // allocation, but this function is called before the memory // allocator has been initialized. return var ( a int8 b uint8 c int16 d uint16 e int32 f uint32 g int64 h uint64 i, i1 float32 j, j1 float64 k, k1 unsafe.Pointer l *uint16 m [4]byte ) type x1t struct { x uint8 } type y1t struct { x1 x1t y uint8 } var x1 x1t var y1 y1t if unsafe.Sizeof(a) != 1 { throw("bad a") } if unsafe.Sizeof(b) != 1 { throw("bad b") } if unsafe.Sizeof(c) != 2 { throw("bad c") } if unsafe.Sizeof(d) != 2 { throw("bad d") } if unsafe.Sizeof(e) != 4 { throw("bad e") } if unsafe.Sizeof(f) != 4 { throw("bad f") } if unsafe.Sizeof(g) != 8 { throw("bad g") } if unsafe.Sizeof(h) != 8 { throw("bad h") } if unsafe.Sizeof(i) != 4 { throw("bad i") } if unsafe.Sizeof(j) != 8 { throw("bad j") } if unsafe.Sizeof(k) != sys.PtrSize { throw("bad k") } if unsafe.Sizeof(l) != sys.PtrSize { throw("bad l") } if unsafe.Sizeof(x1) != 1 { throw("bad unsafe.Sizeof x1") } if unsafe.Offsetof(y1.y) != 1 { throw("bad offsetof y1.y") } if unsafe.Sizeof(y1) != 2 { throw("bad unsafe.Sizeof y1") } if timediv(12345*1000000000+54321, 1000000000, &e) != 12345 || e != 54321 { throw("bad timediv") } var z uint32 z = 1 if !atomic.Cas(&z, 1, 2) { throw("cas1") } if z != 2 { throw("cas2") } z = 4 if atomic.Cas(&z, 5, 6) { throw("cas3") } if z != 4 { throw("cas4") } z = 0xffffffff if !atomic.Cas(&z, 0xffffffff, 0xfffffffe) { throw("cas5") } if z != 0xfffffffe { throw("cas6") } k = unsafe.Pointer(uintptr(0xfedcb123)) if sys.PtrSize == 8 { k = unsafe.Pointer(uintptr(k) << 10) } if casp(&k, nil, nil) { throw("casp1") } k1 = add(k, 1) if !casp(&k, k, k1) { throw("casp2") } if k != k1 { throw("casp3") } m = [4]byte{1, 1, 1, 1} atomic.Or8(&m[1], 0xf0) if m[0] != 1 || m[1] != 0xf1 || m[2] != 1 || m[3] != 1 { throw("atomicor8") } *(*uint64)(unsafe.Pointer(&j)) = ^uint64(0) if j == j { throw("float64nan") } if !(j != j) { throw("float64nan1") } *(*uint64)(unsafe.Pointer(&j1)) = ^uint64(1) if j == j1 { throw("float64nan2") } if !(j != j1) { throw("float64nan3") } *(*uint32)(unsafe.Pointer(&i)) = ^uint32(0) if i == i { throw("float32nan") } if i == i { throw("float32nan1") } *(*uint32)(unsafe.Pointer(&i1)) = ^uint32(1) if i == i1 { throw("float32nan2") } if i == i1 { throw("float32nan3") } testAtomic64() // if _FixedStack != round2(_FixedStack) { // throw("FixedStack is not power-of-2") // } if !checkASM() { throw("assembly checks failed") } }
// getprofile blocks until the next block of profiling data is available // and returns it as a []byte. It is called from the writing goroutine. func (p *cpuProfile) getprofile() []byte { if p == nil { return nil } if p.wholding { // Release previous log to signal handling side. // Loop because we are racing against SetCPUProfileRate(0). for { n := p.handoff if n == 0 { print("runtime: phase error during cpu profile handoff\n") return nil } if n&0x80000000 != 0 { p.wtoggle = 1 - p.wtoggle p.wholding = false p.flushing = true goto Flush } if atomic.Cas(&p.handoff, n, 0) { break } } p.wtoggle = 1 - p.wtoggle p.wholding = false } if p.flushing { goto Flush } if !p.on && p.handoff == 0 { return nil } // Wait for new log. notetsleepg(&p.wait, -1) noteclear(&p.wait) switch n := p.handoff; { case n == 0: print("runtime: phase error during cpu profile wait\n") return nil case n == 0x80000000: p.flushing = true goto Flush default: n &^= 0x80000000 // Return new log to caller. p.wholding = true return uintptrBytes(p.log[p.wtoggle][:n]) } // In flush mode. // Add is no longer being called. We own the log. // Also, p.handoff is non-zero, so flushlog will return false. // Evict the hash table into the log and return it. Flush: for i := range p.hash { b := &p.hash[i] for j := range b.entry { e := &b.entry[j] if e.count > 0 && !p.evict(e, p.flushlog) { // Filled the log. Stop the loop and return what we've got. break Flush } } } // Return pending log data. if p.nlog > 0 { // Note that we're using toggle now, not wtoggle, // because we're working on the log directly. n := p.nlog p.nlog = 0 return uintptrBytes(p.log[p.toggle][:n]) } // Made it through the table without finding anything to log. if !p.eodSent { // We may not have space to append this to the partial log buf, // so we always return a new slice for the end-of-data marker. p.eodSent = true return uintptrBytes(eod[:]) } // Finally done. Clean up and return nil. p.flushing = false if !atomic.Cas(&p.handoff, p.handoff, 0) { print("runtime: profile flush racing with something\n") } return nil }
// gcLockStackBarriers synchronizes with tracebacks of gp's stack // during sigprof for installation or removal of stack barriers. It // blocks until any current sigprof is done tracebacking gp's stack // and then disallows profiling tracebacks of gp's stack. // // This is necessary because a sigprof during barrier installation or // removal could observe inconsistencies between the stkbar array and // the stack itself and crash. // //go:nosplit func gcLockStackBarriers(gp *g) { acquirem() for !atomic.Cas(&gp.stackLock, 0, 1) { osyield() } }
// Allocate a span to use in an MCache. func (c *mcentral) cacheSpan() *mspan { // Deduct credit for this span allocation and sweep if necessary. spanBytes := uintptr(class_to_allocnpages[c.sizeclass]) * _PageSize deductSweepCredit(spanBytes, 0) lock(&c.lock) sg := mheap_.sweepgen retry: var s *mspan for s = c.nonempty.first; s != nil; s = s.next { if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { c.nonempty.remove(s) c.empty.insertBack(s) unlock(&c.lock) s.sweep(true) goto havespan } if s.sweepgen == sg-1 { // the span is being swept by background sweeper, skip continue } // we have a nonempty span that does not require sweeping, allocate from it c.nonempty.remove(s) c.empty.insertBack(s) unlock(&c.lock) goto havespan } for s = c.empty.first; s != nil; s = s.next { if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { // we have an empty span that requires sweeping, // sweep it and see if we can free some space in it c.empty.remove(s) // swept spans are at the end of the list c.empty.insertBack(s) unlock(&c.lock) s.sweep(true) freeIndex := s.nextFreeIndex() if freeIndex != s.nelems { s.freeindex = freeIndex goto havespan } lock(&c.lock) // the span is still empty after sweep // it is already in the empty list, so just retry goto retry } if s.sweepgen == sg-1 { // the span is being swept by background sweeper, skip continue } // already swept empty span, // all subsequent ones must also be either swept or in process of sweeping break } unlock(&c.lock) // Replenish central list if empty. s = c.grow() if s == nil { return nil } lock(&c.lock) c.empty.insertBack(s) unlock(&c.lock) // At this point s is a non-empty span, queued at the end of the empty list, // c is unlocked. havespan: cap := int32((s.npages << _PageShift) / s.elemsize) n := cap - int32(s.allocCount) if n == 0 || s.freeindex == s.nelems || uintptr(s.allocCount) == s.nelems { throw("span has no free objects") } usedBytes := uintptr(s.allocCount) * s.elemsize if usedBytes > 0 { reimburseSweepCredit(usedBytes) } atomic.Xadd64(&memstats.heap_live, int64(spanBytes)-int64(usedBytes)) if trace.enabled { // heap_live changed. traceHeapAlloc() } if gcBlackenEnabled != 0 { // heap_live changed. gcController.revise() } s.incache = true freeByteBase := s.freeindex &^ (64 - 1) whichByte := freeByteBase / 8 // Init alloc bits cache. s.refillAllocCache(whichByte) // Adjust the allocCache so that s.freeindex corresponds to the low bit in // s.allocCache. s.allocCache >>= s.freeindex % 64 return s }
func createfing() { // start the finalizer goroutine exactly once if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) { go runfinq() } }
// SetCPUProfileRate sets the CPU profiling rate to hz samples per second. // If hz <= 0, SetCPUProfileRate turns off profiling. // If the profiler is on, the rate cannot be changed without first turning it off. // // Most clients should use the runtime/pprof package or // the testing package's -test.cpuprofile flag instead of calling // SetCPUProfileRate directly. func SetCPUProfileRate(hz int) { // Clamp hz to something reasonable. if hz < 0 { hz = 0 } if hz > 1000000 { hz = 1000000 } lock(&cpuprofLock) if hz > 0 { if cpuprof == nil { cpuprof = (*cpuProfile)(sysAlloc(unsafe.Sizeof(cpuProfile{}), &memstats.other_sys)) if cpuprof == nil { print("runtime: cpu profiling cannot allocate memory\n") unlock(&cpuprofLock) return } } if cpuprof.on || cpuprof.handoff != 0 { print("runtime: cannot set cpu profile rate until previous profile has finished.\n") unlock(&cpuprofLock) return } cpuprof.on = true // pprof binary header format. // https://github.com/gperftools/gperftools/blob/master/src/profiledata.cc#L119 p := &cpuprof.log[0] p[0] = 0 // count for header p[1] = 3 // depth for header p[2] = 0 // version number p[3] = uintptr(1e6 / hz) // period (microseconds) p[4] = 0 cpuprof.nlog = 5 cpuprof.toggle = 0 cpuprof.wholding = false cpuprof.wtoggle = 0 cpuprof.flushing = false cpuprof.eodSent = false noteclear(&cpuprof.wait) setcpuprofilerate(int32(hz)) } else if cpuprof != nil && cpuprof.on { setcpuprofilerate(0) cpuprof.on = false // Now add is not running anymore, and getprofile owns the entire log. // Set the high bit in cpuprof.handoff to tell getprofile. for { n := cpuprof.handoff if n&0x80000000 != 0 { print("runtime: setcpuprofile(off) twice\n") } if atomic.Cas(&cpuprof.handoff, n, n|0x80000000) { if n == 0 { // we did the transition from 0 -> nonzero so we wake getprofile notewakeup(&cpuprof.wait) } break } } } unlock(&cpuprofLock) }