1
0
mirror of https://github.com/golang/go synced 2024-10-04 06:11:21 -06:00
go/src/runtime/mcentral.c
Russ Cox e29ce175ed malloc in runtime (not used by default)
R=r
DELTA=1551  (1550 added, 0 deleted, 1 changed)
OCL=21404
CL=21538
2008-12-18 15:42:28 -08:00

192 lines
4.1 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 "malloc.h"
static bool MCentral_Grow(MCentral *c);
static void* MCentral_Alloc(MCentral *c);
static void MCentral_Free(MCentral *c, void *v);
// Initialize a single central free list.
void
MCentral_Init(MCentral *c, int32 sizeclass)
{
c->sizeclass = sizeclass;
MSpanList_Init(&c->nonempty);
MSpanList_Init(&c->empty);
}
// Allocate up to n objects from the central free list.
// Return the number of objects allocated.
// The objects are linked together by their first words.
// On return, *pstart points at the first object and *pend at the last.
int32
MCentral_AllocList(MCentral *c, int32 n, void **pstart, void **pend)
{
void *start, *end, *v;
int32 i;
*pstart = nil;
*pend = nil;
lock(c);
// Replenish central list if empty.
if(MSpanList_IsEmpty(&c->nonempty)) {
if(!MCentral_Grow(c)) {
unlock(c);
return 0;
}
}
// Copy from list, up to n.
start = nil;
end = nil;
for(i=0; i<n; i++) {
v = MCentral_Alloc(c);
if(v == nil)
break;
if(start == nil)
start = v;
else
*(void**)end = v;
end = v;
}
c->nfree -= i;
unlock(c);
*pstart = start;
*pend = end;
return i;
}
// Helper: allocate one object from the central free list.
static void*
MCentral_Alloc(MCentral *c)
{
MSpan *s;
void *v;
if(MSpanList_IsEmpty(&c->nonempty))
return nil;
s = c->nonempty.next;
v = s->freelist;
s->freelist = *(void**)v;
if(s->freelist == nil) {
MSpanList_Remove(s);
MSpanList_Insert(&c->empty, s);
}
s->ref++;
return v;
}
// Free n objects back into the central free list.
// Return the number of objects allocated.
// The objects are linked together by their first words.
// On return, *pstart points at the first object and *pend at the last.
void
MCentral_FreeList(MCentral *c, int32 n, void *start, void *end)
{
void *v, *next;
// Assume *(void**)end = nil marks end of list.
// n and end would be useful if we implemented
// the transfer cache optimization in the TODO above.
USED(n);
USED(end);
lock(c);
for(v=start; v; v=next) {
next = *(void**)v;
MCentral_Free(c, v);
}
unlock(c);
}
// Helper: free one object back into the central free list.
static void
MCentral_Free(MCentral *c, void *v)
{
MSpan *s;
PageID p;
// Find span for v.
p = (uintptr)v >> PageShift;
s = MHeap_Lookup(&mheap, p);
if(s == nil || s->ref == 0)
throw("invalid free");
// Move to nonempty if necessary.
if(s->freelist == nil) {
MSpanList_Remove(s);
MSpanList_Insert(&c->nonempty, s);
}
// Add v back to s's free list.
*(void**)v = s->freelist;
s->freelist = v;
c->nfree++;
// If s is completely freed, return it to the heap.
if(--s->ref == 0) {
MSpanList_Remove(s);
c->nfree -= (s->npages << PageShift) / class_to_size[c->sizeclass];
unlock(c);
MHeap_Free(&mheap, s);
lock(c);
}
}
// Fetch a new span from the heap and
// carve into objects for the free list.
static bool
MCentral_Grow(MCentral *c)
{
int32 n, npages, size;
void **tail;
byte *p, *end;
MSpan *s;
unlock(c);
npages = class_to_allocnpages[c->sizeclass];
s = MHeap_Alloc(&mheap, npages, c->sizeclass);
if(s == nil) {
// TODO(rsc): Log out of memory
lock(c);
return false;
}
// Carve span into sequence of blocks.
tail = &s->freelist;
p = (byte*)(s->start << PageShift);
end = p + (npages << PageShift);
size = class_to_size[c->sizeclass];
n = 0;
for(; p + size <= end; p += size) {
*tail = p;
tail = (void**)p;
n++;
}
*tail = nil;
lock(c);
c->nfree += n;
MSpanList_Insert(&c->nonempty, s);
return true;
}