summaryrefslogtreecommitdiff
path: root/src/runtime/mcentral.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/mcentral.c')
-rw-r--r--src/runtime/mcentral.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/src/runtime/mcentral.c b/src/runtime/mcentral.c
new file mode 100644
index 000000000..fe6bcfeb1
--- /dev/null
+++ b/src/runtime/mcentral.c
@@ -0,0 +1,214 @@
+// 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).
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "malloc.h"
+
+static MSpan* MCentral_Grow(MCentral *c);
+
+// 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->lock);
+ 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·MSpanList_Remove(s);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
+ runtime·MSpan_Sweep(s, true);
+ goto havespan;
+ }
+ 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
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
+ 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->lock);
+ runtime·MSpan_Sweep(s, true);
+ if(s->freelist != nil)
+ goto havespan;
+ runtime·lock(&c->lock);
+ // the span is still empty after sweep
+ // it is already in the empty list, so just 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;
+ }
+ runtime·unlock(&c->lock);
+
+ // Replenish central list if empty.
+ s = MCentral_Grow(c);
+ if(s == nil)
+ return nil;
+ runtime·lock(&c->lock);
+ runtime·MSpanList_InsertBack(&c->empty, s);
+ runtime·unlock(&c->lock);
+
+havespan:
+ // At this point s is a non-empty span, queued at the end of the empty list,
+ // c is unlocked.
+ 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");
+ s->incache = true;
+ return s;
+}
+
+// Return span from an MCache.
+void
+runtime·MCentral_UncacheSpan(MCentral *c, MSpan *s)
+{
+ int32 cap, n;
+
+ runtime·lock(&c->lock);
+
+ s->incache = false;
+
+ if(s->ref == 0)
+ runtime·throw("uncaching full span");
+
+ cap = (s->npages << PageShift) / s->elemsize;
+ n = cap - s->ref;
+ if(n > 0) {
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_Insert(&c->nonempty, s);
+ }
+ runtime·unlock(&c->lock);
+}
+
+// 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.
+// If preserve=true, don't return the span to heap nor relink in MCentral lists;
+// caller takes care of it.
+bool
+runtime·MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end, bool preserve)
+{
+ bool wasempty;
+
+ if(s->incache)
+ runtime·throw("freespan into cached span");
+
+ // Add the objects back to s's free list.
+ wasempty = s->freelist == nil;
+ end->next = s->freelist;
+ s->freelist = start;
+ s->ref -= n;
+
+ if(preserve) {
+ // preserve is set only when called from MCentral_CacheSpan above,
+ // the span must be in the empty list.
+ if(s->next == nil)
+ runtime·throw("can't preserve unlinked span");
+ runtime·atomicstore(&s->sweepgen, runtime·mheap.sweepgen);
+ return false;
+ }
+
+ runtime·lock(&c->lock);
+
+ // Move to nonempty if necessary.
+ if(wasempty) {
+ runtime·MSpanList_Remove(s);
+ runtime·MSpanList_Insert(&c->nonempty, s);
+ }
+
+ // 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->lock);
+ return false;
+ }
+
+ // s is completely freed, return it to the heap.
+ runtime·MSpanList_Remove(s);
+ s->needzero = 1;
+ s->freelist = nil;
+ runtime·unlock(&c->lock);
+ runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
+ runtime·MHeap_Free(&runtime·mheap, s, 0);
+ return true;
+}
+
+// Fetch a new span from the heap and carve into objects for the free list.
+static MSpan*
+MCentral_Grow(MCentral *c)
+{
+ uintptr size, npages, i, n;
+ MLink **tailp, *v;
+ byte *p;
+ MSpan *s;
+
+ npages = runtime·class_to_allocnpages[c->sizeclass];
+ size = runtime·class_to_size[c->sizeclass];
+ n = (npages << PageShift) / size;
+ s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0, 1);
+ if(s == nil)
+ return nil;
+
+ // 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));
+ return s;
+}