// 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, MLink **pfirst) { MLink *first, *last, *v; int32 i; lock(c); // Replenish central list if empty. if(MSpanList_IsEmpty(&c->nonempty)) { if(!MCentral_Grow(c)) { unlock(c); *pfirst = nil; return 0; } } // Copy from list, up to n. // First one is guaranteed to work, because we just grew the list. first = MCentral_Alloc(c); last = first; for(i=1; inext = v; last = v; } last->next = nil; c->nfree -= i; unlock(c); *pfirst = first; return i; } // Helper: allocate one object from the central free list. static void* MCentral_Alloc(MCentral *c) { MSpan *s; MLink *v; if(MSpanList_IsEmpty(&c->nonempty)) return nil; s = c->nonempty.next; s->ref++; v = s->freelist; s->freelist = v->next; if(s->freelist == nil) { MSpanList_Remove(s); MSpanList_Insert(&c->empty, s); } 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, MLink *start) { MLink *v, *next; // Assume next == nil marks end of list. // n and end would be useful if we implemented // the transfer cache optimization in the TODO above. USED(n); lock(c); for(v=start; v; v=next) { next = v->next; 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 page; MLink *p, *next; // Find span for v. page = (uintptr)v >> PageShift; s = MHeap_Lookup(&mheap, page); 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. p = v; p->next = s->freelist; s->freelist = p; c->nfree++; // If s is completely freed, return it to the heap. if(--s->ref == 0) { MSpanList_Remove(s); // Freed blocks are zeroed except for the link pointer. // Zero the link pointers so that the page is all zero. for(p=s->freelist; p; p=next) { next = p->next; p->next = nil; } s->freelist = nil; 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 i, n, npages, size; MLink **tailp, *v; byte *p; 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. tailp = &s->freelist; p = (byte*)(s->start << PageShift); size = class_to_size[c->sizeclass]; n = (npages << PageShift) / (size + RefcountOverhead); s->gcref = (uint32*)(p + size*n); for(i=0; inext; p += size; } *tailp = nil; lock(c); c->nfree += n; MSpanList_Insert(&c->nonempty, s); return true; }