// 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_GOARCH.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; inext; p += size; } *tailp = nil; runtime·markspan((byte*)(s->start<npages<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<npages<