// Variant of sync/atomic's TestUnaligned64: func TestUnaligned64(t *testing.T) { // Unaligned 64-bit atomics on 32-bit systems are // a continual source of pain. Test that on 32-bit systems they crash // instead of failing silently. switch runtime.GOARCH { default: if unsafe.Sizeof(int(0)) != 4 { t.Skip("test only runs on 32-bit systems") } case "amd64p32": // amd64p32 can handle unaligned atomics. t.Skipf("test not needed on %v", runtime.GOARCH) } x := make([]uint32, 4) up64 := (*uint64)(unsafe.Pointer(&x[1])) // misaligned p64 := (*int64)(unsafe.Pointer(&x[1])) // misaligned shouldPanic(t, "Load64", func() { atomic.Load64(up64) }) shouldPanic(t, "Loadint64", func() { atomic.Loadint64(p64) }) shouldPanic(t, "Store64", func() { atomic.Store64(up64, 0) }) shouldPanic(t, "Xadd64", func() { atomic.Xadd64(up64, 1) }) shouldPanic(t, "Xchg64", func() { atomic.Xchg64(up64, 1) }) shouldPanic(t, "Cas64", func() { atomic.Cas64(up64, 1, 2) }) }
// gcParkAssist puts the current goroutine on the assist queue and parks. // // gcParkAssist returns whether the assist is now satisfied. If it // returns false, the caller must retry the assist. // //go:nowritebarrier func gcParkAssist() bool { lock(&work.assistQueue.lock) // If the GC cycle finished while we were getting the lock, // exit the assist. The cycle can't finish while we hold the // lock. if atomic.Load(&gcBlackenEnabled) == 0 { unlock(&work.assistQueue.lock) return true } gp := getg() oldHead, oldTail := work.assistQueue.head, work.assistQueue.tail if oldHead == 0 { work.assistQueue.head.set(gp) } else { oldTail.ptr().schedlink.set(gp) } work.assistQueue.tail.set(gp) gp.schedlink.set(nil) // Recheck for background credit now that this G is in // the queue, but can still back out. This avoids a // race in case background marking has flushed more // credit since we checked above. if atomic.Loadint64(&gcController.bgScanCredit) > 0 { work.assistQueue.head = oldHead work.assistQueue.tail = oldTail if oldTail != 0 { oldTail.ptr().schedlink.set(nil) } unlock(&work.assistQueue.lock) return false } // Park. goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlockGC, 2) return true }
// gcAssistAlloc performs GC work to make gp's assist debt positive. // gp must be the calling user gorountine. // // This must be called with preemption enabled. func gcAssistAlloc(gp *g) { // Don't assist in non-preemptible contexts. These are // generally fragile and won't allow the assist to block. if getg() == gp.m.g0 { return } if mp := getg().m; mp.locks > 0 || mp.preemptoff != "" { return } retry: // Compute the amount of scan work we need to do to make the // balance positive. When the required amount of work is low, // we over-assist to build up credit for future allocations // and amortize the cost of assisting. debtBytes := -gp.gcAssistBytes scanWork := int64(gcController.assistWorkPerByte * float64(debtBytes)) if scanWork < gcOverAssistWork { scanWork = gcOverAssistWork debtBytes = int64(gcController.assistBytesPerWork * float64(scanWork)) } // Steal as much credit as we can from the background GC's // scan credit. This is racy and may drop the background // credit below 0 if two mutators steal at the same time. This // will just cause steals to fail until credit is accumulated // again, so in the long run it doesn't really matter, but we // do have to handle the negative credit case. bgScanCredit := atomic.Loadint64(&gcController.bgScanCredit) stolen := int64(0) if bgScanCredit > 0 { if bgScanCredit < scanWork { stolen = bgScanCredit gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(stolen)) } else { stolen = scanWork gp.gcAssistBytes += debtBytes } atomic.Xaddint64(&gcController.bgScanCredit, -stolen) scanWork -= stolen if scanWork == 0 { // We were able to steal all of the credit we // needed. return } } // Perform assist work systemstack(func() { gcAssistAlloc1(gp, scanWork) // The user stack may have moved, so this can't touch // anything on it until it returns from systemstack. }) completed := gp.param != nil gp.param = nil if completed { gcMarkDone() } if gp.gcAssistBytes < 0 { // We were unable steal enough credit or perform // enough work to pay off the assist debt. We need to // do one of these before letting the mutator allocate // more to prevent over-allocation. // // If this is because we were preempted, reschedule // and try some more. if gp.preempt { Gosched() goto retry } // Add this G to an assist queue and park. When the GC // has more background credit, it will satisfy queued // assists before flushing to the global credit pool. // // Note that this does *not* get woken up when more // work is added to the work list. The theory is that // there wasn't enough work to do anyway, so we might // as well let background marking take care of the // work that is available. if !gcParkAssist() { goto retry } // At this point either background GC has satisfied // this G's assist debt, or the GC cycle is over. } }
// gcAssistAlloc performs GC work to make gp's assist debt positive. // gp must be the calling user gorountine. // // This must be called with preemption enabled. //go:nowritebarrier func gcAssistAlloc(gp *g) { // Don't assist in non-preemptible contexts. These are // generally fragile and won't allow the assist to block. if getg() == gp.m.g0 { return } if mp := getg().m; mp.locks > 0 || mp.preemptoff != "" { return } // Compute the amount of scan work we need to do to make the // balance positive. We over-assist to build up credit for // future allocations and amortize the cost of assisting. debtBytes := -gp.gcAssistBytes + gcOverAssistBytes scanWork := int64(gcController.assistWorkPerByte * float64(debtBytes)) retry: // Steal as much credit as we can from the background GC's // scan credit. This is racy and may drop the background // credit below 0 if two mutators steal at the same time. This // will just cause steals to fail until credit is accumulated // again, so in the long run it doesn't really matter, but we // do have to handle the negative credit case. bgScanCredit := atomic.Loadint64(&gcController.bgScanCredit) stolen := int64(0) if bgScanCredit > 0 { if bgScanCredit < scanWork { stolen = bgScanCredit gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(stolen)) } else { stolen = scanWork gp.gcAssistBytes += debtBytes } atomic.Xaddint64(&gcController.bgScanCredit, -stolen) scanWork -= stolen if scanWork == 0 { // We were able to steal all of the credit we // needed. return } } // Perform assist work completed := false systemstack(func() { if atomic.Load(&gcBlackenEnabled) == 0 { // The gcBlackenEnabled check in malloc races with the // store that clears it but an atomic check in every malloc // would be a performance hit. // Instead we recheck it here on the non-preemptable system // stack to determine if we should preform an assist. // GC is done, so ignore any remaining debt. gp.gcAssistBytes = 0 return } // Track time spent in this assist. Since we're on the // system stack, this is non-preemptible, so we can // just measure start and end time. startTime := nanotime() decnwait := atomic.Xadd(&work.nwait, -1) if decnwait == work.nproc { println("runtime: work.nwait =", decnwait, "work.nproc=", work.nproc) throw("nwait > work.nprocs") } // drain own cached work first in the hopes that it // will be more cache friendly. gcw := &getg().m.p.ptr().gcw workDone := gcDrainN(gcw, scanWork) // If we are near the end of the mark phase // dispose of the gcw. if gcBlackenPromptly { gcw.dispose() } // Record that we did this much scan work. // // Back out the number of bytes of assist credit that // this scan work counts for. The "1+" is a poor man's // round-up, to ensure this adds credit even if // assistBytesPerWork is very low. gp.gcAssistBytes += 1 + int64(gcController.assistBytesPerWork*float64(workDone)) // If this is the last worker and we ran out of work, // signal a completion point. incnwait := atomic.Xadd(&work.nwait, +1) if incnwait > work.nproc { println("runtime: work.nwait=", incnwait, "work.nproc=", work.nproc, "gcBlackenPromptly=", gcBlackenPromptly) throw("work.nwait > work.nproc") } if incnwait == work.nproc && !gcMarkWorkAvailable(nil) { // This has reached a background completion // point. completed = true } duration := nanotime() - startTime _p_ := gp.m.p.ptr() _p_.gcAssistTime += duration if _p_.gcAssistTime > gcAssistTimeSlack { atomic.Xaddint64(&gcController.assistTime, _p_.gcAssistTime) _p_.gcAssistTime = 0 } }) if completed { gcMarkDone() } if gp.gcAssistBytes < 0 { // We were unable steal enough credit or perform // enough work to pay off the assist debt. We need to // do one of these before letting the mutator allocate // more to prevent over-allocation. // // If this is because we were preempted, reschedule // and try some more. if gp.preempt { Gosched() goto retry } // Add this G to an assist queue and park. When the GC // has more background credit, it will satisfy queued // assists before flushing to the global credit pool. // // Note that this does *not* get woken up when more // work is added to the work list. The theory is that // there wasn't enough work to do anyway, so we might // as well let background marking take care of the // work that is available. lock(&work.assistQueue.lock) // If the GC cycle is over, just return. This is the // likely path if we completed above. We do this // under the lock to prevent a GC cycle from ending // between this check and queuing the assist. if atomic.Load(&gcBlackenEnabled) == 0 { unlock(&work.assistQueue.lock) return } oldHead, oldTail := work.assistQueue.head, work.assistQueue.tail if oldHead == 0 { work.assistQueue.head.set(gp) } else { oldTail.ptr().schedlink.set(gp) } work.assistQueue.tail.set(gp) gp.schedlink.set(nil) // Recheck for background credit now that this G is in // the queue, but can still back out. This avoids a // race in case background marking has flushed more // credit since we checked above. if atomic.Loadint64(&gcController.bgScanCredit) > 0 { work.assistQueue.head = oldHead work.assistQueue.tail = oldTail if oldTail != 0 { oldTail.ptr().schedlink.set(nil) } unlock(&work.assistQueue.lock) goto retry } // Park for real. goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlock, 2) // At this point either background GC has satisfied // this G's assist debt, or the GC cycle is over. } }