00d86ac99f
From-SVN: r212837
308 lines
7.5 KiB
C
308 lines
7.5 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.
|
|
|
|
// Central free lists.
|
|
//
|
|
// See malloc.h 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).
|
|
//
|
|
// TODO(rsc): tcmalloc uses a "transfer cache" to split the list
|
|
// into sections of class_to_transfercount[sizeclass] objects
|
|
// so that it is faster to move those lists between MCaches and MCentrals.
|
|
|
|
#include "runtime.h"
|
|
#include "arch.h"
|
|
#include "malloc.h"
|
|
|
|
static bool MCentral_Grow(MCentral *c);
|
|
static void MCentral_Free(MCentral *c, MLink *v);
|
|
static void MCentral_ReturnToHeap(MCentral *c, MSpan *s);
|
|
|
|
// Initialize a single central free list.
|
|
void
|
|
runtime_MCentral_Init(MCentral *c, int32 sizeclass)
|
|
{
|
|
c->sizeclass = sizeclass;
|
|
runtime_MSpanList_Init(&c->nonempty);
|
|
runtime_MSpanList_Init(&c->empty);
|
|
}
|
|
|
|
// Allocate a span to use in an MCache.
|
|
MSpan*
|
|
runtime_MCentral_CacheSpan(MCentral *c)
|
|
{
|
|
MSpan *s;
|
|
int32 cap, n;
|
|
uint32 sg;
|
|
|
|
runtime_lock(c);
|
|
sg = runtime_mheap.sweepgen;
|
|
retry:
|
|
for(s = c->nonempty.next; s != &c->nonempty; s = s->next) {
|
|
if(s->sweepgen == sg-2 && runtime_cas(&s->sweepgen, sg-2, sg-1)) {
|
|
runtime_unlock(c);
|
|
runtime_MSpan_Sweep(s);
|
|
runtime_lock(c);
|
|
// the span could have been moved to heap, retry
|
|
goto retry;
|
|
}
|
|
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
|
|
goto havespan;
|
|
}
|
|
|
|
for(s = c->empty.next; s != &c->empty; s = s->next) {
|
|
if(s->sweepgen == sg-2 && runtime_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
|
|
runtime_MSpanList_Remove(s);
|
|
// swept spans are at the end of the list
|
|
runtime_MSpanList_InsertBack(&c->empty, s);
|
|
runtime_unlock(c);
|
|
runtime_MSpan_Sweep(s);
|
|
runtime_lock(c);
|
|
// the span could be moved to nonempty or heap, 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;
|
|
}
|
|
|
|
// Replenish central list if empty.
|
|
if(!MCentral_Grow(c)) {
|
|
runtime_unlock(c);
|
|
return nil;
|
|
}
|
|
goto retry;
|
|
|
|
havespan:
|
|
cap = (s->npages << PageShift) / s->elemsize;
|
|
n = cap - s->ref;
|
|
if(n == 0)
|
|
runtime_throw("empty span");
|
|
if(s->freelist == nil)
|
|
runtime_throw("freelist empty");
|
|
c->nfree -= n;
|
|
runtime_MSpanList_Remove(s);
|
|
runtime_MSpanList_InsertBack(&c->empty, s);
|
|
s->incache = true;
|
|
runtime_unlock(c);
|
|
return s;
|
|
}
|
|
|
|
// Return span from an MCache.
|
|
void
|
|
runtime_MCentral_UncacheSpan(MCentral *c, MSpan *s)
|
|
{
|
|
MLink *v;
|
|
int32 cap, n;
|
|
|
|
runtime_lock(c);
|
|
|
|
s->incache = false;
|
|
|
|
// Move any explicitly freed items from the freebuf to the freelist.
|
|
while((v = s->freebuf) != nil) {
|
|
s->freebuf = v->next;
|
|
runtime_markfreed(v);
|
|
v->next = s->freelist;
|
|
s->freelist = v;
|
|
s->ref--;
|
|
}
|
|
|
|
if(s->ref == 0) {
|
|
// Free back to heap. Unlikely, but possible.
|
|
MCentral_ReturnToHeap(c, s); // unlocks c
|
|
return;
|
|
}
|
|
|
|
cap = (s->npages << PageShift) / s->elemsize;
|
|
n = cap - s->ref;
|
|
if(n > 0) {
|
|
c->nfree += n;
|
|
runtime_MSpanList_Remove(s);
|
|
runtime_MSpanList_Insert(&c->nonempty, s);
|
|
}
|
|
runtime_unlock(c);
|
|
}
|
|
|
|
// Free the list of objects back into the central free list c.
|
|
// Called from runtime_free.
|
|
void
|
|
runtime_MCentral_FreeList(MCentral *c, MLink *start)
|
|
{
|
|
MLink *next;
|
|
|
|
runtime_lock(c);
|
|
for(; start != nil; start = next) {
|
|
next = start->next;
|
|
MCentral_Free(c, start);
|
|
}
|
|
runtime_unlock(c);
|
|
}
|
|
|
|
// Helper: free one object back into the central free list.
|
|
// Caller must hold lock on c on entry. Holds lock on exit.
|
|
static void
|
|
MCentral_Free(MCentral *c, MLink *v)
|
|
{
|
|
MSpan *s;
|
|
|
|
// Find span for v.
|
|
s = runtime_MHeap_Lookup(&runtime_mheap, v);
|
|
if(s == nil || s->ref == 0)
|
|
runtime_throw("invalid free");
|
|
if(s->sweepgen != runtime_mheap.sweepgen)
|
|
runtime_throw("free into unswept span");
|
|
|
|
// If the span is currently being used unsynchronized by an MCache,
|
|
// we can't modify the freelist. Add to the freebuf instead. The
|
|
// items will get moved to the freelist when the span is returned
|
|
// by the MCache.
|
|
if(s->incache) {
|
|
v->next = s->freebuf;
|
|
s->freebuf = v;
|
|
return;
|
|
}
|
|
|
|
// Move span to nonempty if necessary.
|
|
if(s->freelist == nil) {
|
|
runtime_MSpanList_Remove(s);
|
|
runtime_MSpanList_Insert(&c->nonempty, s);
|
|
}
|
|
|
|
// Add the object to span's free list.
|
|
runtime_markfreed(v);
|
|
v->next = s->freelist;
|
|
s->freelist = v;
|
|
s->ref--;
|
|
c->nfree++;
|
|
|
|
// If s is completely freed, return it to the heap.
|
|
if(s->ref == 0) {
|
|
MCentral_ReturnToHeap(c, s); // unlocks c
|
|
runtime_lock(c);
|
|
}
|
|
}
|
|
|
|
// Free n objects from a span s back into the central free list c.
|
|
// Called during sweep.
|
|
// Returns true if the span was returned to heap. Sets sweepgen to
|
|
// the latest generation.
|
|
bool
|
|
runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end)
|
|
{
|
|
if(s->incache)
|
|
runtime_throw("freespan into cached span");
|
|
runtime_lock(c);
|
|
|
|
// Move to nonempty if necessary.
|
|
if(s->freelist == nil) {
|
|
runtime_MSpanList_Remove(s);
|
|
runtime_MSpanList_Insert(&c->nonempty, s);
|
|
}
|
|
|
|
// Add the objects back to s's free list.
|
|
end->next = s->freelist;
|
|
s->freelist = start;
|
|
s->ref -= n;
|
|
c->nfree += n;
|
|
|
|
// 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.)
|
|
runtime_atomicstore(&s->sweepgen, runtime_mheap.sweepgen);
|
|
|
|
if(s->ref != 0) {
|
|
runtime_unlock(c);
|
|
return false;
|
|
}
|
|
|
|
// s is completely freed, return it to the heap.
|
|
MCentral_ReturnToHeap(c, s); // unlocks c
|
|
return true;
|
|
}
|
|
|
|
void
|
|
runtime_MGetSizeClassInfo(int32 sizeclass, uintptr *sizep, int32 *npagesp, int32 *nobj)
|
|
{
|
|
int32 size;
|
|
int32 npages;
|
|
|
|
npages = runtime_class_to_allocnpages[sizeclass];
|
|
size = runtime_class_to_size[sizeclass];
|
|
*npagesp = npages;
|
|
*sizep = size;
|
|
*nobj = (npages << PageShift) / size;
|
|
}
|
|
|
|
// Fetch a new span from the heap and
|
|
// carve into objects for the free list.
|
|
static bool
|
|
MCentral_Grow(MCentral *c)
|
|
{
|
|
int32 i, n, npages;
|
|
uintptr size;
|
|
MLink **tailp, *v;
|
|
byte *p;
|
|
MSpan *s;
|
|
|
|
runtime_unlock(c);
|
|
runtime_MGetSizeClassInfo(c->sizeclass, &size, &npages, &n);
|
|
s = runtime_MHeap_Alloc(&runtime_mheap, npages, c->sizeclass, 0, 1);
|
|
if(s == nil) {
|
|
// TODO(rsc): Log out of memory
|
|
runtime_lock(c);
|
|
return false;
|
|
}
|
|
|
|
// Carve span into sequence of blocks.
|
|
tailp = &s->freelist;
|
|
p = (byte*)(s->start << PageShift);
|
|
s->limit = p + size*n;
|
|
for(i=0; i<n; i++) {
|
|
v = (MLink*)p;
|
|
*tailp = v;
|
|
tailp = &v->next;
|
|
p += size;
|
|
}
|
|
*tailp = nil;
|
|
runtime_markspan((byte*)(s->start<<PageShift), size, n, size*n < (s->npages<<PageShift));
|
|
|
|
runtime_lock(c);
|
|
c->nfree += n;
|
|
runtime_MSpanList_Insert(&c->nonempty, s);
|
|
return true;
|
|
}
|
|
|
|
// Return s to the heap. s must be unused (s->ref == 0). Unlocks c.
|
|
static void
|
|
MCentral_ReturnToHeap(MCentral *c, MSpan *s)
|
|
{
|
|
int32 size;
|
|
|
|
size = runtime_class_to_size[c->sizeclass];
|
|
runtime_MSpanList_Remove(s);
|
|
s->needzero = 1;
|
|
s->freelist = nil;
|
|
if(s->ref != 0)
|
|
runtime_throw("ref wrong");
|
|
c->nfree -= (s->npages << PageShift) / size;
|
|
runtime_unlock(c);
|
|
runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
|
|
runtime_MHeap_Free(&runtime_mheap, s, 0);
|
|
}
|