gcc/libgo/runtime/mheap.c
Ian Lance Taylor 58f7dab40d runtime: copy mstats code from Go 1.7 runtime
This replaces mem.go and the C runtime_ReadMemStats function with the Go
    1.7 mstats.go.
    
    The GCStats code is commented out for now.  The corresponding gccgo code
    is in runtime/mgc0.c.
    
    The variables memstats and worldsema are shared between the Go code and
    the C code, but are not exported.  To make this work, add temporary
    accessor functions acquireWorldsema, releaseWorldsema, getMstats (the
    latter known as mstats in the C code).
    
    Check the preemptoff field of m when allocating and when considering
    whether to start a GC.  This works with the new stopTheWorld and
    startTheWorld functions in Go, which are essentially the Go 1.7
    versions.
    
    Change the compiler to stack allocate closures when compiling the
    runtime package.  Within the runtime packages closures do not escape.
    This is similar to what the gc compiler does, except that the gc
    compiler, when compiling the runtime package, gives an error if escape
    analysis shows that a closure does escape.  I added this here because
    the Go version of ReadMemStats calls systemstack with a closure, and
    having that allocate memory was causing some tests that measure memory
    allocations to fail.
    
    Reviewed-on: https://go-review.googlesource.com/30972

From-SVN: r241124
2016-10-13 15:24:50 +00:00

958 lines
25 KiB
C

// 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.
// Page heap.
//
// See malloc.h for overview.
//
// When a MSpan is in the heap free list, state == MSpanFree
// and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span.
//
// When a MSpan is allocated, state == MSpanInUse
// and heapmap(i) == span for all s->start <= i < s->start+s->npages.
#include "runtime.h"
#include "arch.h"
#include "malloc.h"
static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32);
static bool MHeap_Grow(MHeap*, uintptr);
static void MHeap_FreeLocked(MHeap*, MSpan*);
static MSpan *MHeap_AllocLarge(MHeap*, uintptr);
static MSpan *BestFit(MSpan*, uintptr, MSpan*);
static void
RecordSpan(void *vh, byte *p)
{
MHeap *h;
MSpan *s;
MSpan **all;
uint32 cap;
h = vh;
s = (MSpan*)p;
if(h->nspan >= h->nspancap) {
cap = 64*1024/sizeof(all[0]);
if(cap < h->nspancap*3/2)
cap = h->nspancap*3/2;
all = (MSpan**)runtime_SysAlloc(cap*sizeof(all[0]), &mstats()->other_sys);
if(all == nil)
runtime_throw("runtime: cannot allocate memory");
if(h->allspans) {
runtime_memmove(all, h->allspans, h->nspancap*sizeof(all[0]));
// Don't free the old array if it's referenced by sweep.
// See the comment in mgc0.c.
if(h->allspans != runtime_mheap.sweepspans)
runtime_SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats()->other_sys);
}
h->allspans = all;
h->nspancap = cap;
}
h->allspans[h->nspan++] = s;
}
// Initialize the heap; fetch memory using alloc.
void
runtime_MHeap_Init(MHeap *h)
{
MStats *pmstats;
uint32 i;
pmstats = mstats();
runtime_FixAlloc_Init(&h->spanalloc, sizeof(MSpan), RecordSpan, h, &pmstats->mspan_sys);
runtime_FixAlloc_Init(&h->cachealloc, sizeof(MCache), nil, nil, &pmstats->mcache_sys);
runtime_FixAlloc_Init(&h->specialfinalizeralloc, sizeof(SpecialFinalizer), nil, nil, &pmstats->other_sys);
runtime_FixAlloc_Init(&h->specialprofilealloc, sizeof(SpecialProfile), nil, nil, &pmstats->other_sys);
// h->mapcache needs no init
for(i=0; i<nelem(h->free); i++) {
runtime_MSpanList_Init(&h->free[i]);
runtime_MSpanList_Init(&h->busy[i]);
}
runtime_MSpanList_Init(&h->freelarge);
runtime_MSpanList_Init(&h->busylarge);
for(i=0; i<nelem(h->central); i++)
runtime_MCentral_Init(&h->central[i], i);
}
void
runtime_MHeap_MapSpans(MHeap *h)
{
uintptr pagesize;
uintptr n;
// Map spans array, PageSize at a time.
n = (uintptr)h->arena_used;
n -= (uintptr)h->arena_start;
n = n / PageSize * sizeof(h->spans[0]);
n = ROUND(n, PageSize);
pagesize = getpagesize();
n = ROUND(n, pagesize);
if(h->spans_mapped >= n)
return;
runtime_SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, h->arena_reserved, &mstats()->other_sys);
h->spans_mapped = n;
}
// Sweeps spans in list until reclaims at least npages into heap.
// Returns the actual number of pages reclaimed.
static uintptr
MHeap_ReclaimList(MHeap *h, MSpan *list, uintptr npages)
{
MSpan *s;
uintptr n;
uint32 sg;
n = 0;
sg = runtime_mheap.sweepgen;
retry:
for(s = list->next; s != list; s = s->next) {
if(s->sweepgen == sg-2 && runtime_cas(&s->sweepgen, sg-2, sg-1)) {
runtime_MSpanList_Remove(s);
// swept spans are at the end of the list
runtime_MSpanList_InsertBack(list, s);
runtime_unlock(h);
n += runtime_MSpan_Sweep(s);
runtime_lock(h);
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;
}
// Sweeps and reclaims at least npage pages into heap.
// Called before allocating npage pages.
static void
MHeap_Reclaim(MHeap *h, uintptr npage)
{
uintptr reclaimed, n;
// First try to sweep busy spans with large objects of size >= npage,
// this has good chances of reclaiming the necessary space.
for(n=npage; n < nelem(h->busy); n++) {
if(MHeap_ReclaimList(h, &h->busy[n], npage))
return; // Bingo!
}
// Then -- even larger objects.
if(MHeap_ReclaimList(h, &h->busylarge, npage))
return; // Bingo!
// Now try smaller objects.
// One such object is not enough, so we need to reclaim several of them.
reclaimed = 0;
for(n=0; n < npage && n < nelem(h->busy); n++) {
reclaimed += MHeap_ReclaimList(h, &h->busy[n], npage-reclaimed);
if(reclaimed >= npage)
return;
}
// Now sweep everything that is not yet swept.
runtime_unlock(h);
for(;;) {
n = runtime_sweepone();
if(n == (uintptr)-1) // all spans are swept
break;
reclaimed += n;
if(reclaimed >= npage)
break;
}
runtime_lock(h);
}
// Allocate a new span of npage pages from the heap
// and record its size class in the HeapMap and HeapMapCache.
MSpan*
runtime_MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large, bool needzero)
{
MStats *pmstats;
MSpan *s;
runtime_lock(h);
pmstats = mstats();
pmstats->heap_alloc += (intptr)runtime_m()->mcache->local_cachealloc;
runtime_m()->mcache->local_cachealloc = 0;
s = MHeap_AllocLocked(h, npage, sizeclass);
if(s != nil) {
pmstats->heap_inuse += npage<<PageShift;
if(large) {
pmstats->heap_objects++;
pmstats->heap_alloc += npage<<PageShift;
// Swept spans are at the end of lists.
if(s->npages < nelem(h->free))
runtime_MSpanList_InsertBack(&h->busy[s->npages], s);
else
runtime_MSpanList_InsertBack(&h->busylarge, s);
}
}
runtime_unlock(h);
if(s != nil) {
if(needzero && s->needzero)
runtime_memclr((byte*)(s->start<<PageShift), s->npages<<PageShift);
s->needzero = 0;
}
return s;
}
static MSpan*
MHeap_AllocLocked(MHeap *h, uintptr npage, int32 sizeclass)
{
uintptr n;
MSpan *s, *t;
PageID p;
// To prevent excessive heap growth, before allocating n pages
// we need to sweep and reclaim at least n pages.
if(!h->sweepdone)
MHeap_Reclaim(h, npage);
// Try in fixed-size lists up to max.
for(n=npage; n < nelem(h->free); n++) {
if(!runtime_MSpanList_IsEmpty(&h->free[n])) {
s = h->free[n].next;
goto HaveSpan;
}
}
// Best fit in list of large spans.
if((s = MHeap_AllocLarge(h, npage)) == nil) {
if(!MHeap_Grow(h, npage))
return nil;
if((s = MHeap_AllocLarge(h, npage)) == nil)
return nil;
}
HaveSpan:
// Mark span in use.
if(s->state != MSpanFree)
runtime_throw("MHeap_AllocLocked - MSpan not free");
if(s->npages < npage)
runtime_throw("MHeap_AllocLocked - bad npages");
runtime_MSpanList_Remove(s);
runtime_atomicstore(&s->sweepgen, h->sweepgen);
s->state = MSpanInUse;
mstats()->heap_idle -= s->npages<<PageShift;
mstats()->heap_released -= s->npreleased<<PageShift;
if(s->npreleased > 0)
runtime_SysUsed((void*)(s->start<<PageShift), s->npages<<PageShift);
s->npreleased = 0;
if(s->npages > npage) {
// Trim extra and put it back in the heap.
t = runtime_FixAlloc_Alloc(&h->spanalloc);
runtime_MSpan_Init(t, s->start + npage, s->npages - npage);
s->npages = npage;
p = t->start;
p -= ((uintptr)h->arena_start>>PageShift);
if(p > 0)
h->spans[p-1] = s;
h->spans[p] = t;
h->spans[p+t->npages-1] = t;
t->needzero = s->needzero;
runtime_atomicstore(&t->sweepgen, h->sweepgen);
t->state = MSpanInUse;
MHeap_FreeLocked(h, t);
t->unusedsince = s->unusedsince; // preserve age
}
s->unusedsince = 0;
// Record span info, because gc needs to be
// able to map interior pointer to containing span.
s->sizeclass = sizeclass;
s->elemsize = (sizeclass==0 ? s->npages<<PageShift : (uintptr)runtime_class_to_size[sizeclass]);
s->types.compression = MTypes_Empty;
p = s->start;
p -= ((uintptr)h->arena_start>>PageShift);
for(n=0; n<npage; n++)
h->spans[p+n] = s;
return s;
}
// Allocate a span of exactly npage pages from the list of large spans.
static MSpan*
MHeap_AllocLarge(MHeap *h, uintptr npage)
{
return BestFit(&h->freelarge, npage, nil);
}
// Search list for smallest span with >= npage pages.
// If there are multiple smallest spans, take the one
// with the earliest starting address.
static MSpan*
BestFit(MSpan *list, uintptr npage, MSpan *best)
{
MSpan *s;
for(s=list->next; s != list; s=s->next) {
if(s->npages < npage)
continue;
if(best == nil
|| s->npages < best->npages
|| (s->npages == best->npages && s->start < best->start))
best = s;
}
return best;
}
// Try to add at least npage pages of memory to the heap,
// returning whether it worked.
static bool
MHeap_Grow(MHeap *h, uintptr npage)
{
uintptr ask;
void *v;
MSpan *s;
PageID p;
// Ask for a big chunk, to reduce the number of mappings
// the operating system needs to track; also amortizes
// the overhead of an operating system mapping.
// Allocate a multiple of 64kB (16 pages).
npage = (npage+15)&~15;
ask = npage<<PageShift;
if(ask < HeapAllocChunk)
ask = HeapAllocChunk;
v = runtime_MHeap_SysAlloc(h, ask);
if(v == nil) {
if(ask > (npage<<PageShift)) {
ask = npage<<PageShift;
v = runtime_MHeap_SysAlloc(h, ask);
}
if(v == nil) {
runtime_printf("runtime: out of memory: cannot allocate %D-byte block (%D in use)\n", (uint64)ask, mstats()->heap_sys);
return false;
}
}
// Create a fake "in use" span and free it, so that the
// right coalescing happens.
s = runtime_FixAlloc_Alloc(&h->spanalloc);
runtime_MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift);
p = s->start;
p -= ((uintptr)h->arena_start>>PageShift);
h->spans[p] = s;
h->spans[p + s->npages - 1] = s;
runtime_atomicstore(&s->sweepgen, h->sweepgen);
s->state = MSpanInUse;
MHeap_FreeLocked(h, s);
return true;
}
// Look up the span at the given address.
// Address is guaranteed to be in map
// and is guaranteed to be start or end of span.
MSpan*
runtime_MHeap_Lookup(MHeap *h, void *v)
{
uintptr p;
p = (uintptr)v;
p -= (uintptr)h->arena_start;
return h->spans[p >> PageShift];
}
// Look up the span at the given address.
// Address is *not* guaranteed to be in map
// and may be anywhere in the span.
// Map entries for the middle of a span are only
// valid for allocated spans. Free spans may have
// other garbage in their middles, so we have to
// check for that.
MSpan*
runtime_MHeap_LookupMaybe(MHeap *h, void *v)
{
MSpan *s;
PageID p, q;
if((byte*)v < h->arena_start || (byte*)v >= h->arena_used)
return nil;
p = (uintptr)v>>PageShift;
q = p;
q -= (uintptr)h->arena_start >> PageShift;
s = h->spans[q];
if(s == nil || p < s->start || (uintptr)v >= s->limit || s->state != MSpanInUse)
return nil;
return s;
}
// Free the span back into the heap.
void
runtime_MHeap_Free(MHeap *h, MSpan *s, int32 acct)
{
MStats *pmstats;
runtime_lock(h);
pmstats = mstats();
pmstats->heap_alloc += (intptr)runtime_m()->mcache->local_cachealloc;
runtime_m()->mcache->local_cachealloc = 0;
pmstats->heap_inuse -= s->npages<<PageShift;
if(acct) {
pmstats->heap_alloc -= s->npages<<PageShift;
pmstats->heap_objects--;
}
MHeap_FreeLocked(h, s);
runtime_unlock(h);
}
static void
MHeap_FreeLocked(MHeap *h, MSpan *s)
{
MSpan *t;
PageID p;
s->types.compression = MTypes_Empty;
if(s->state != MSpanInUse || s->ref != 0 || s->sweepgen != h->sweepgen) {
runtime_printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d sweepgen %d/%d\n",
s, s->start<<PageShift, s->state, s->ref, s->sweepgen, h->sweepgen);
runtime_throw("MHeap_FreeLocked - invalid free");
}
mstats()->heap_idle += s->npages<<PageShift;
s->state = MSpanFree;
runtime_MSpanList_Remove(s);
// Stamp newly unused spans. The scavenger will use that
// info to potentially give back some pages to the OS.
s->unusedsince = runtime_nanotime();
s->npreleased = 0;
// Coalesce with earlier, later spans.
p = s->start;
p -= (uintptr)h->arena_start >> PageShift;
if(p > 0 && (t = h->spans[p-1]) != nil && t->state != MSpanInUse) {
s->start = t->start;
s->npages += t->npages;
s->npreleased = t->npreleased; // absorb released pages
s->needzero |= t->needzero;
p -= t->npages;
h->spans[p] = s;
runtime_MSpanList_Remove(t);
t->state = MSpanDead;
runtime_FixAlloc_Free(&h->spanalloc, t);
}
if((p+s->npages)*sizeof(h->spans[0]) < h->spans_mapped && (t = h->spans[p+s->npages]) != nil && t->state != MSpanInUse) {
s->npages += t->npages;
s->npreleased += t->npreleased;
s->needzero |= t->needzero;
h->spans[p + s->npages - 1] = s;
runtime_MSpanList_Remove(t);
t->state = MSpanDead;
runtime_FixAlloc_Free(&h->spanalloc, t);
}
// Insert s into appropriate list.
if(s->npages < nelem(h->free))
runtime_MSpanList_Insert(&h->free[s->npages], s);
else
runtime_MSpanList_Insert(&h->freelarge, s);
}
static void
forcegchelper(void *vnote)
{
Note *note = (Note*)vnote;
runtime_gc(1);
runtime_notewakeup(note);
}
static uintptr
scavengelist(MSpan *list, uint64 now, uint64 limit)
{
uintptr released, sumreleased, start, end, pagesize;
MSpan *s;
if(runtime_MSpanList_IsEmpty(list))
return 0;
sumreleased = 0;
for(s=list->next; s != list; s=s->next) {
if((now - s->unusedsince) > limit && s->npreleased != s->npages) {
released = (s->npages - s->npreleased) << PageShift;
mstats()->heap_released += released;
sumreleased += released;
s->npreleased = s->npages;
start = s->start << PageShift;
end = start + (s->npages << PageShift);
// Round start up and end down to ensure we
// are acting on entire pages.
pagesize = getpagesize();
start = ROUND(start, pagesize);
end &= ~(pagesize - 1);
if(end > start)
runtime_SysUnused((void*)start, end - start);
}
}
return sumreleased;
}
static void
scavenge(int32 k, uint64 now, uint64 limit)
{
uint32 i;
uintptr sumreleased;
MHeap *h;
h = &runtime_mheap;
sumreleased = 0;
for(i=0; i < nelem(h->free); i++)
sumreleased += scavengelist(&h->free[i], now, limit);
sumreleased += scavengelist(&h->freelarge, now, limit);
if(runtime_debug.gctrace > 0) {
if(sumreleased > 0)
runtime_printf("scvg%d: %D MB released\n", k, (uint64)sumreleased>>20);
runtime_printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n",
k, mstats()->heap_inuse>>20, mstats()->heap_idle>>20, mstats()->heap_sys>>20,
mstats()->heap_released>>20, (mstats()->heap_sys - mstats()->heap_released)>>20);
}
}
// Release (part of) unused memory to OS.
// Goroutine created at startup.
// Loop forever.
void
runtime_MHeap_Scavenger(void* dummy)
{
G *g;
MHeap *h;
uint64 tick, now, forcegc, limit;
int64 unixnow;
uint32 k;
Note note, *notep;
USED(dummy);
g = runtime_g();
g->issystem = true;
g->isbackground = true;
// If we go two minutes without a garbage collection, force one to run.
forcegc = 2*60*1e9;
// If a span goes unused for 5 minutes after a garbage collection,
// we hand it back to the operating system.
limit = 5*60*1e9;
// Make wake-up period small enough for the sampling to be correct.
if(forcegc < limit)
tick = forcegc/2;
else
tick = limit/2;
h = &runtime_mheap;
for(k=0;; k++) {
runtime_noteclear(&note);
runtime_notetsleepg(&note, tick);
runtime_lock(h);
unixnow = runtime_unixnanotime();
if(unixnow - mstats()->last_gc > forcegc) {
runtime_unlock(h);
// The scavenger can not block other goroutines,
// otherwise deadlock detector can fire spuriously.
// GC blocks other goroutines via the runtime_worldsema.
runtime_noteclear(&note);
notep = &note;
__go_go(forcegchelper, (void*)notep);
runtime_notetsleepg(&note, -1);
if(runtime_debug.gctrace > 0)
runtime_printf("scvg%d: GC forced\n", k);
runtime_lock(h);
}
now = runtime_nanotime();
scavenge(k, now, limit);
runtime_unlock(h);
}
}
void runtime_debug_freeOSMemory(void) __asm__("runtime_debug.freeOSMemory");
void
runtime_debug_freeOSMemory(void)
{
runtime_gc(2); // force GC and do eager sweep
runtime_lock(&runtime_mheap);
scavenge(-1, ~(uintptr)0, 0);
runtime_unlock(&runtime_mheap);
}
// Initialize a new span with the given start and npages.
void
runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages)
{
span->next = nil;
span->prev = nil;
span->start = start;
span->npages = npages;
span->freelist = nil;
span->ref = 0;
span->sizeclass = 0;
span->incache = false;
span->elemsize = 0;
span->state = MSpanDead;
span->unusedsince = 0;
span->npreleased = 0;
span->types.compression = MTypes_Empty;
span->speciallock.key = 0;
span->specials = nil;
span->needzero = 0;
span->freebuf = nil;
}
// Initialize an empty doubly-linked list.
void
runtime_MSpanList_Init(MSpan *list)
{
list->state = MSpanListHead;
list->next = list;
list->prev = list;
}
void
runtime_MSpanList_Remove(MSpan *span)
{
if(span->prev == nil && span->next == nil)
return;
span->prev->next = span->next;
span->next->prev = span->prev;
span->prev = nil;
span->next = nil;
}
bool
runtime_MSpanList_IsEmpty(MSpan *list)
{
return list->next == list;
}
void
runtime_MSpanList_Insert(MSpan *list, MSpan *span)
{
if(span->next != nil || span->prev != nil) {
runtime_printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev);
runtime_throw("MSpanList_Insert");
}
span->next = list->next;
span->prev = list;
span->next->prev = span;
span->prev->next = span;
}
void
runtime_MSpanList_InsertBack(MSpan *list, MSpan *span)
{
if(span->next != nil || span->prev != nil) {
runtime_printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev);
runtime_throw("MSpanList_Insert");
}
span->next = list;
span->prev = list->prev;
span->next->prev = span;
span->prev->next = span;
}
// Adds the special record s to the list of special records for
// the object p. All fields of s should be filled in except for
// offset & next, which this routine will fill in.
// Returns true if the special was successfully added, false otherwise.
// (The add will fail only if a record with the same p and s->kind
// already exists.)
static bool
addspecial(void *p, Special *s)
{
MSpan *span;
Special **t, *x;
uintptr offset;
byte kind;
span = runtime_MHeap_LookupMaybe(&runtime_mheap, p);
if(span == nil)
runtime_throw("addspecial on invalid pointer");
// Ensure that the span is swept.
// GC accesses specials list w/o locks. And it's just much safer.
runtime_m()->locks++;
runtime_MSpan_EnsureSwept(span);
offset = (uintptr)p - (span->start << PageShift);
kind = s->kind;
runtime_lock(&span->speciallock);
// Find splice point, check for existing record.
t = &span->specials;
while((x = *t) != nil) {
if(offset == x->offset && kind == x->kind) {
runtime_unlock(&span->speciallock);
runtime_m()->locks--;
return false; // already exists
}
if(offset < x->offset || (offset == x->offset && kind < x->kind))
break;
t = &x->next;
}
// Splice in record, fill in offset.
s->offset = offset;
s->next = x;
*t = s;
runtime_unlock(&span->speciallock);
runtime_m()->locks--;
return true;
}
// Removes the Special record of the given kind for the object p.
// Returns the record if the record existed, nil otherwise.
// The caller must FixAlloc_Free the result.
static Special*
removespecial(void *p, byte kind)
{
MSpan *span;
Special *s, **t;
uintptr offset;
span = runtime_MHeap_LookupMaybe(&runtime_mheap, p);
if(span == nil)
runtime_throw("removespecial on invalid pointer");
// Ensure that the span is swept.
// GC accesses specials list w/o locks. And it's just much safer.
runtime_m()->locks++;
runtime_MSpan_EnsureSwept(span);
offset = (uintptr)p - (span->start << PageShift);
runtime_lock(&span->speciallock);
t = &span->specials;
while((s = *t) != nil) {
// This function is used for finalizers only, so we don't check for
// "interior" specials (p must be exactly equal to s->offset).
if(offset == s->offset && kind == s->kind) {
*t = s->next;
runtime_unlock(&span->speciallock);
runtime_m()->locks--;
return s;
}
t = &s->next;
}
runtime_unlock(&span->speciallock);
runtime_m()->locks--;
return nil;
}
// Adds a finalizer to the object p. Returns true if it succeeded.
bool
runtime_addfinalizer(void *p, FuncVal *f, const FuncType *ft, const PtrType *ot)
{
SpecialFinalizer *s;
runtime_lock(&runtime_mheap.speciallock);
s = runtime_FixAlloc_Alloc(&runtime_mheap.specialfinalizeralloc);
runtime_unlock(&runtime_mheap.speciallock);
s->kind = KindSpecialFinalizer;
s->fn = f;
s->ft = ft;
s->ot = ot;
if(addspecial(p, s))
return true;
// There was an old finalizer
runtime_lock(&runtime_mheap.speciallock);
runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, s);
runtime_unlock(&runtime_mheap.speciallock);
return false;
}
// Removes the finalizer (if any) from the object p.
void
runtime_removefinalizer(void *p)
{
SpecialFinalizer *s;
s = (SpecialFinalizer*)removespecial(p, KindSpecialFinalizer);
if(s == nil)
return; // there wasn't a finalizer to remove
runtime_lock(&runtime_mheap.speciallock);
runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, s);
runtime_unlock(&runtime_mheap.speciallock);
}
// Set the heap profile bucket associated with addr to b.
void
runtime_setprofilebucket(void *p, Bucket *b)
{
SpecialProfile *s;
runtime_lock(&runtime_mheap.speciallock);
s = runtime_FixAlloc_Alloc(&runtime_mheap.specialprofilealloc);
runtime_unlock(&runtime_mheap.speciallock);
s->kind = KindSpecialProfile;
s->b = b;
if(!addspecial(p, s))
runtime_throw("setprofilebucket: profile already set");
}
// Do whatever cleanup needs to be done to deallocate s. It has
// already been unlinked from the MSpan specials list.
// Returns true if we should keep working on deallocating p.
bool
runtime_freespecial(Special *s, void *p, uintptr size, bool freed)
{
SpecialFinalizer *sf;
SpecialProfile *sp;
switch(s->kind) {
case KindSpecialFinalizer:
sf = (SpecialFinalizer*)s;
runtime_queuefinalizer(p, sf->fn, sf->ft, sf->ot);
runtime_lock(&runtime_mheap.speciallock);
runtime_FixAlloc_Free(&runtime_mheap.specialfinalizeralloc, sf);
runtime_unlock(&runtime_mheap.speciallock);
return false; // don't free p until finalizer is done
case KindSpecialProfile:
sp = (SpecialProfile*)s;
runtime_MProf_Free(sp->b, size, freed);
runtime_lock(&runtime_mheap.speciallock);
runtime_FixAlloc_Free(&runtime_mheap.specialprofilealloc, sp);
runtime_unlock(&runtime_mheap.speciallock);
return true;
default:
runtime_throw("bad special kind");
return true;
}
}
// Free all special records for p.
void
runtime_freeallspecials(MSpan *span, void *p, uintptr size)
{
Special *s, **t, *list;
uintptr offset;
if(span->sweepgen != runtime_mheap.sweepgen)
runtime_throw("runtime: freeallspecials: unswept span");
// first, collect all specials into the list; then, free them
// this is required to not cause deadlock between span->specialLock and proflock
list = nil;
offset = (uintptr)p - (span->start << PageShift);
runtime_lock(&span->speciallock);
t = &span->specials;
while((s = *t) != nil) {
if(offset + size <= s->offset)
break;
if(offset <= s->offset) {
*t = s->next;
s->next = list;
list = s;
} else
t = &s->next;
}
runtime_unlock(&span->speciallock);
while(list != nil) {
s = list;
list = s->next;
if(!runtime_freespecial(s, p, size, true))
runtime_throw("can't explicitly free an object with a finalizer");
}
}
// Split an allocated span into two equal parts.
void
runtime_MHeap_SplitSpan(MHeap *h, MSpan *s)
{
MSpan *t;
MCentral *c;
uintptr i;
uintptr npages;
PageID p;
if(s->state != MSpanInUse)
runtime_throw("MHeap_SplitSpan on a free span");
if(s->sizeclass != 0 && s->ref != 1)
runtime_throw("MHeap_SplitSpan doesn't have an allocated object");
npages = s->npages;
// remove the span from whatever list it is in now
if(s->sizeclass > 0) {
// must be in h->central[x].mempty
c = &h->central[s->sizeclass];
runtime_lock(c);
runtime_MSpanList_Remove(s);
runtime_unlock(c);
runtime_lock(h);
} else {
// must be in h->busy/busylarge
runtime_lock(h);
runtime_MSpanList_Remove(s);
}
// heap is locked now
if(npages == 1) {
// convert span of 1 PageSize object to a span of 2 PageSize/2 objects.
s->ref = 2;
s->sizeclass = runtime_SizeToClass(PageSize/2);
s->elemsize = PageSize/2;
} else {
// convert span of n>1 pages into two spans of n/2 pages each.
if((s->npages & 1) != 0)
runtime_throw("MHeap_SplitSpan on an odd size span");
// compute position in h->spans
p = s->start;
p -= (uintptr)h->arena_start >> PageShift;
// Allocate a new span for the first half.
t = runtime_FixAlloc_Alloc(&h->spanalloc);
runtime_MSpan_Init(t, s->start, npages/2);
t->limit = (uintptr)((t->start + npages/2) << PageShift);
t->state = MSpanInUse;
t->elemsize = npages << (PageShift - 1);
t->sweepgen = s->sweepgen;
if(t->elemsize <= MaxSmallSize) {
t->sizeclass = runtime_SizeToClass(t->elemsize);
t->ref = 1;
}
// the old span holds the second half.
s->start += npages/2;
s->npages = npages/2;
s->elemsize = npages << (PageShift - 1);
if(s->elemsize <= MaxSmallSize) {
s->sizeclass = runtime_SizeToClass(s->elemsize);
s->ref = 1;
}
// update span lookup table
for(i = p; i < p + npages/2; i++)
h->spans[i] = t;
}
// place the span into a new list
if(s->sizeclass > 0) {
runtime_unlock(h);
c = &h->central[s->sizeclass];
runtime_lock(c);
// swept spans are at the end of the list
runtime_MSpanList_InsertBack(&c->mempty, s);
runtime_unlock(c);
} else {
// Swept spans are at the end of lists.
if(s->npages < nelem(h->free))
runtime_MSpanList_InsertBack(&h->busy[s->npages], s);
else
runtime_MSpanList_InsertBack(&h->busylarge, s);
runtime_unlock(h);
}
}