4f4a855d82
Reviewed-on: https://go-review.googlesource.com/c/158019 gotools/: * Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release. (GOTOOLS_TEST_TIMEOUT): Increase to 600. (check-runtime): Export LD_LIBRARY_PATH before computing GOARCH and GOOS. (check-vet): Copy golang.org/x/tools into check-vet-dir. * Makefile.in: Regenerate. gcc/testsuite/: * go.go-torture/execute/names-1.go: Stop using debug/xcoff, which is no longer externally visible. From-SVN: r268084
276 lines
7.2 KiB
Go
276 lines
7.2 KiB
Go
// 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.
|
|
|
|
// Central free lists.
|
|
//
|
|
// See malloc.go for an overview.
|
|
//
|
|
// The mcentral doesn't actually contain the list of free objects; the mspan does.
|
|
// Each mcentral is two lists of mspans: those with free objects (c->nonempty)
|
|
// and those that are completely allocated (c->empty).
|
|
|
|
package runtime
|
|
|
|
import "runtime/internal/atomic"
|
|
|
|
// Central list of free objects of a given size.
|
|
//
|
|
//go:notinheap
|
|
type mcentral struct {
|
|
lock mutex
|
|
spanclass spanClass
|
|
nonempty mSpanList // list of spans with a free object, ie a nonempty free list
|
|
empty mSpanList // list of spans with no free objects (or cached in an mcache)
|
|
|
|
// nmalloc is the cumulative count of objects allocated from
|
|
// this mcentral, assuming all spans in mcaches are
|
|
// fully-allocated. Written atomically, read under STW.
|
|
nmalloc uint64
|
|
}
|
|
|
|
// Initialize a single central free list.
|
|
func (c *mcentral) init(spc spanClass) {
|
|
c.spanclass = spc
|
|
c.nonempty.init()
|
|
c.empty.init()
|
|
}
|
|
|
|
// 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.spanclass.sizeclass()]) * _PageSize
|
|
deductSweepCredit(spanBytes, 0)
|
|
|
|
lock(&c.lock)
|
|
traceDone := false
|
|
if trace.enabled {
|
|
traceGCSweepStart()
|
|
}
|
|
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)
|
|
|
|
// With gccgo's conservative GC, the returned span may
|
|
// now be full. See the comments in mspan.sweep.
|
|
if uintptr(s.allocCount) == s.nelems {
|
|
s.freeindex = s.nelems
|
|
lock(&c.lock)
|
|
goto retry
|
|
}
|
|
|
|
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
|
|
}
|
|
if trace.enabled {
|
|
traceGCSweepDone()
|
|
traceDone = true
|
|
}
|
|
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:
|
|
if trace.enabled && !traceDone {
|
|
traceGCSweepDone()
|
|
}
|
|
n := int(s.nelems) - int(s.allocCount)
|
|
if n == 0 || s.freeindex == s.nelems || uintptr(s.allocCount) == s.nelems {
|
|
throw("span has no free objects")
|
|
}
|
|
// Assume all objects from this span will be allocated in the
|
|
// mcache. If it gets uncached, we'll adjust this.
|
|
atomic.Xadd64(&c.nmalloc, int64(n))
|
|
usedBytes := uintptr(s.allocCount) * s.elemsize
|
|
atomic.Xadd64(&memstats.heap_live, int64(spanBytes)-int64(usedBytes))
|
|
if trace.enabled {
|
|
// heap_live changed.
|
|
traceHeapAlloc()
|
|
}
|
|
if gcBlackenEnabled != 0 {
|
|
// heap_live changed.
|
|
gcController.revise()
|
|
}
|
|
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
|
|
}
|
|
|
|
// Return span from an mcache.
|
|
func (c *mcentral) uncacheSpan(s *mspan) {
|
|
if s.allocCount == 0 {
|
|
throw("uncaching span but s.allocCount == 0")
|
|
}
|
|
|
|
sg := mheap_.sweepgen
|
|
stale := s.sweepgen == sg+1
|
|
if stale {
|
|
// Span was cached before sweep began. It's our
|
|
// responsibility to sweep it.
|
|
//
|
|
// Set sweepgen to indicate it's not cached but needs
|
|
// sweeping and can't be allocated from. sweep will
|
|
// set s.sweepgen to indicate s is swept.
|
|
atomic.Store(&s.sweepgen, sg-1)
|
|
} else {
|
|
// Indicate that s is no longer cached.
|
|
atomic.Store(&s.sweepgen, sg)
|
|
}
|
|
|
|
n := int(s.nelems) - int(s.allocCount)
|
|
if n > 0 {
|
|
// cacheSpan updated alloc assuming all objects on s
|
|
// were going to be allocated. Adjust for any that
|
|
// weren't. We must do this before potentially
|
|
// sweeping the span.
|
|
atomic.Xadd64(&c.nmalloc, -int64(n))
|
|
|
|
lock(&c.lock)
|
|
c.empty.remove(s)
|
|
c.nonempty.insert(s)
|
|
if !stale {
|
|
// mCentral_CacheSpan conservatively counted
|
|
// unallocated slots in heap_live. Undo this.
|
|
//
|
|
// If this span was cached before sweep, then
|
|
// heap_live was totally recomputed since
|
|
// caching this span, so we don't do this for
|
|
// stale spans.
|
|
atomic.Xadd64(&memstats.heap_live, -int64(n)*int64(s.elemsize))
|
|
}
|
|
unlock(&c.lock)
|
|
}
|
|
|
|
if stale {
|
|
// Now that s is in the right mcentral list, we can
|
|
// sweep it.
|
|
s.sweep(false)
|
|
}
|
|
}
|
|
|
|
// freeSpan updates c and s after sweeping s.
|
|
// It sets s's sweepgen to the latest generation,
|
|
// and, based on the number of free objects in s,
|
|
// moves s to the appropriate list of c or returns it
|
|
// to the heap.
|
|
// freeSpan reports whether s was returned to the heap.
|
|
// If preserve=true, it does not move s (the caller
|
|
// must take care of it).
|
|
func (c *mcentral) freeSpan(s *mspan, preserve bool, wasempty bool) bool {
|
|
if sg := mheap_.sweepgen; s.sweepgen == sg+1 || s.sweepgen == sg+3 {
|
|
throw("freeSpan given cached span")
|
|
}
|
|
s.needzero = 1
|
|
|
|
if preserve {
|
|
// preserve is set only when called from (un)cacheSpan above,
|
|
// the span must be in the empty list.
|
|
if !s.inList() {
|
|
throw("can't preserve unlinked span")
|
|
}
|
|
atomic.Store(&s.sweepgen, mheap_.sweepgen)
|
|
return false
|
|
}
|
|
|
|
lock(&c.lock)
|
|
|
|
// Move to nonempty if necessary.
|
|
if wasempty {
|
|
c.empty.remove(s)
|
|
c.nonempty.insert(s)
|
|
}
|
|
|
|
// delay updating sweepgen until here. This is the signal that
|
|
// the span may be used in an mcache, so it must come after the
|
|
// linked list operations above (actually, just after the
|
|
// lock of c above.)
|
|
atomic.Store(&s.sweepgen, mheap_.sweepgen)
|
|
|
|
if s.allocCount != 0 {
|
|
unlock(&c.lock)
|
|
return false
|
|
}
|
|
|
|
c.nonempty.remove(s)
|
|
unlock(&c.lock)
|
|
mheap_.freeSpan(s, false)
|
|
return true
|
|
}
|
|
|
|
// grow allocates a new empty span from the heap and initializes it for c's size class.
|
|
func (c *mcentral) grow() *mspan {
|
|
npages := uintptr(class_to_allocnpages[c.spanclass.sizeclass()])
|
|
size := uintptr(class_to_size[c.spanclass.sizeclass()])
|
|
n := (npages << _PageShift) / size
|
|
|
|
s := mheap_.alloc(npages, c.spanclass, false, true)
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
|
|
p := s.base()
|
|
s.limit = p + size*n
|
|
|
|
heapBitsForAddr(s.base()).initSpan(s)
|
|
return s
|
|
}
|