diff options
Diffstat (limited to 'src/pkg/runtime/mheap.c')
| -rw-r--r-- | src/pkg/runtime/mheap.c | 506 | 
1 files changed, 443 insertions, 63 deletions
| diff --git a/src/pkg/runtime/mheap.c b/src/pkg/runtime/mheap.c index fc80c2600..7e83eb283 100644 --- a/src/pkg/runtime/mheap.c +++ b/src/pkg/runtime/mheap.c @@ -41,7 +41,10 @@ RecordSpan(void *vh, byte *p)  			runtime·throw("runtime: cannot allocate memory");  		if(h->allspans) {  			runtime·memmove(all, h->allspans, h->nspancap*sizeof(all[0])); -			runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys); +			// Don't free the old array if it's referenced by sweep. +			// See the comment in mgc0.c. +			if(h->allspans != runtime·mheap.sweepspans) +				runtime·SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys);  		}  		h->allspans = all;  		h->nspancap = cap; @@ -57,10 +60,15 @@ runtime·MHeap_Init(MHeap *h)  	runtime·FixAlloc_Init(&h->spanalloc, sizeof(MSpan), RecordSpan, h, &mstats.mspan_sys);  	runtime·FixAlloc_Init(&h->cachealloc, sizeof(MCache), nil, nil, &mstats.mcache_sys); +	runtime·FixAlloc_Init(&h->specialfinalizeralloc, sizeof(SpecialFinalizer), nil, nil, &mstats.other_sys); +	runtime·FixAlloc_Init(&h->specialprofilealloc, sizeof(SpecialProfile), nil, nil, &mstats.other_sys);  	// h->mapcache needs no init -	for(i=0; i<nelem(h->free); i++) +	for(i=0; i<nelem(h->free); i++) {  		runtime·MSpanList_Init(&h->free[i]); -	runtime·MSpanList_Init(&h->large); +		runtime·MSpanList_Init(&h->busy[i]); +	} +	runtime·MSpanList_Init(&h->freelarge); +	runtime·MSpanList_Init(&h->busylarge);  	for(i=0; i<nelem(h->central); i++)  		runtime·MCentral_Init(&h->central[i], i);  } @@ -72,20 +80,95 @@ runtime·MHeap_MapSpans(MHeap *h)  	// Map spans array, PageSize at a time.  	n = (uintptr)h->arena_used; -	if(sizeof(void*) == 8) -		n -= (uintptr)h->arena_start; +	n -= (uintptr)h->arena_start;  	n = n / PageSize * sizeof(h->spans[0]); -	n = ROUND(n, PageSize); +	n = ROUND(n, PhysPageSize);  	if(h->spans_mapped >= n)  		return; -	runtime·SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, &mstats.other_sys); +	runtime·SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, h->arena_reserved, &mstats.other_sys);  	h->spans_mapped = n;  } +// Sweeps spans in list until reclaims at least npages into heap. +// Returns the actual number of pages reclaimed. +static uintptr +MHeap_ReclaimList(MHeap *h, MSpan *list, uintptr npages) +{ +	MSpan *s; +	uintptr n; +	uint32 sg; + +	n = 0; +	sg = runtime·mheap.sweepgen; +retry: +	for(s = list->next; s != list; s = s->next) { +		if(s->sweepgen == sg-2 && runtime·cas(&s->sweepgen, sg-2, sg-1)) { +			runtime·MSpanList_Remove(s); +			// swept spans are at the end of the list +			runtime·MSpanList_InsertBack(list, s); +			runtime·unlock(h); +			n += runtime·MSpan_Sweep(s); +			runtime·lock(h); +			if(n >= npages) +				return n; +			// the span could have been moved elsewhere +			goto retry; +		} +		if(s->sweepgen == sg-1) { +			// the span is being sweept by background sweeper, skip +			continue; +		} +		// already swept empty span, +		// all subsequent ones must also be either swept or in process of sweeping +		break; +	} +	return n; +} + +// Sweeps and reclaims at least npage pages into heap. +// Called before allocating npage pages. +static void +MHeap_Reclaim(MHeap *h, uintptr npage) +{ +	uintptr reclaimed, n; + +	// First try to sweep busy spans with large objects of size >= npage, +	// this has good chances of reclaiming the necessary space. +	for(n=npage; n < nelem(h->busy); n++) { +		if(MHeap_ReclaimList(h, &h->busy[n], npage)) +			return;  // Bingo! +	} + +	// Then -- even larger objects. +	if(MHeap_ReclaimList(h, &h->busylarge, npage)) +		return;  // Bingo! + +	// Now try smaller objects. +	// One such object is not enough, so we need to reclaim several of them. +	reclaimed = 0; +	for(n=0; n < npage && n < nelem(h->busy); n++) { +		reclaimed += MHeap_ReclaimList(h, &h->busy[n], npage-reclaimed); +		if(reclaimed >= npage) +			return; +	} + +	// Now sweep everything that is not yet swept. +	runtime·unlock(h); +	for(;;) { +		n = runtime·sweepone(); +		if(n == -1)  // all spans are swept +			break; +		reclaimed += n; +		if(reclaimed >= npage) +			break; +	} +	runtime·lock(h); +} +  // Allocate a new span of npage pages from the heap  // and record its size class in the HeapMap and HeapMapCache.  MSpan* -runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed) +runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large, bool needzero)  {  	MSpan *s; @@ -95,14 +178,22 @@ runtime·MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32  	s = MHeap_AllocLocked(h, npage, sizeclass);  	if(s != nil) {  		mstats.heap_inuse += npage<<PageShift; -		if(acct) { +		if(large) {  			mstats.heap_objects++;  			mstats.heap_alloc += npage<<PageShift; +			// Swept spans are at the end of lists. +			if(s->npages < nelem(h->free)) +				runtime·MSpanList_InsertBack(&h->busy[s->npages], s); +			else +				runtime·MSpanList_InsertBack(&h->busylarge, s);  		}  	}  	runtime·unlock(h); -	if(s != nil && *(uintptr*)(s->start<<PageShift) != 0 && zeroed) -		runtime·memclr((byte*)(s->start<<PageShift), s->npages<<PageShift); +	if(s != nil) { +		if(needzero && s->needzero) +			runtime·memclr((byte*)(s->start<<PageShift), s->npages<<PageShift); +		s->needzero = 0; +	}  	return s;  } @@ -113,6 +204,11 @@ MHeap_AllocLocked(MHeap *h, uintptr npage, int32 sizeclass)  	MSpan *s, *t;  	PageID p; +	// To prevent excessive heap growth, before allocating n pages +	// we need to sweep and reclaim at least n pages. +	if(!h->sweepdone) +		MHeap_Reclaim(h, npage); +  	// Try in fixed-size lists up to max.  	for(n=npage; n < nelem(h->free); n++) {  		if(!runtime·MSpanList_IsEmpty(&h->free[n])) { @@ -136,29 +232,12 @@ HaveSpan:  	if(s->npages < npage)  		runtime·throw("MHeap_AllocLocked - bad npages");  	runtime·MSpanList_Remove(s); +	runtime·atomicstore(&s->sweepgen, h->sweepgen);  	s->state = MSpanInUse;  	mstats.heap_idle -= s->npages<<PageShift;  	mstats.heap_released -= s->npreleased<<PageShift; -	if(s->npreleased > 0) { -		// We have called runtime·SysUnused with these pages, and on -		// Unix systems it called madvise.  At this point at least -		// some BSD-based kernels will return these pages either as -		// zeros or with the old data.  For our caller, the first word -		// in the page indicates whether the span contains zeros or -		// not (this word was set when the span was freed by -		// MCentral_Free or runtime·MCentral_FreeSpan).  If the first -		// page in the span is returned as zeros, and some subsequent -		// page is returned with the old data, then we will be -		// returning a span that is assumed to be all zeros, but the -		// actual data will not be all zeros.  Avoid that problem by -		// explicitly marking the span as not being zeroed, just in -		// case.  The beadbead constant we use here means nothing, it -		// is just a unique constant not seen elsewhere in the -		// runtime, as a clue in case it turns up unexpectedly in -		// memory or in a stack trace. +	if(s->npreleased > 0)  		runtime·SysUsed((void*)(s->start<<PageShift), s->npages<<PageShift); -		*(uintptr*)(s->start<<PageShift) = (uintptr)0xbeadbeadbeadbeadULL; -	}  	s->npreleased = 0;  	if(s->npages > npage) { @@ -167,13 +246,13 @@ HaveSpan:  		runtime·MSpan_Init(t, s->start + npage, s->npages - npage);  		s->npages = npage;  		p = t->start; -		if(sizeof(void*) == 8) -			p -= ((uintptr)h->arena_start>>PageShift); +		p -= ((uintptr)h->arena_start>>PageShift);  		if(p > 0)  			h->spans[p-1] = s;  		h->spans[p] = t;  		h->spans[p+t->npages-1] = t; -		*(uintptr*)(t->start<<PageShift) = *(uintptr*)(s->start<<PageShift);  // copy "needs zeroing" mark +		t->needzero = s->needzero; +		runtime·atomicstore(&t->sweepgen, h->sweepgen);  		t->state = MSpanInUse;  		MHeap_FreeLocked(h, t);  		t->unusedsince = s->unusedsince; // preserve age @@ -186,8 +265,7 @@ HaveSpan:  	s->elemsize = (sizeclass==0 ? s->npages<<PageShift : runtime·class_to_size[sizeclass]);  	s->types.compression = MTypes_Empty;  	p = s->start; -	if(sizeof(void*) == 8) -		p -= ((uintptr)h->arena_start>>PageShift); +	p -= ((uintptr)h->arena_start>>PageShift);  	for(n=0; n<npage; n++)  		h->spans[p+n] = s;  	return s; @@ -197,7 +275,7 @@ HaveSpan:  static MSpan*  MHeap_AllocLarge(MHeap *h, uintptr npage)  { -	return BestFit(&h->large, npage, nil); +	return BestFit(&h->freelarge, npage, nil);  }  // Search list for smallest span with >= npage pages. @@ -255,10 +333,10 @@ MHeap_Grow(MHeap *h, uintptr npage)  	s = runtime·FixAlloc_Alloc(&h->spanalloc);  	runtime·MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift);  	p = s->start; -	if(sizeof(void*) == 8) -		p -= ((uintptr)h->arena_start>>PageShift); +	p -= ((uintptr)h->arena_start>>PageShift);  	h->spans[p] = s;  	h->spans[p + s->npages - 1] = s; +	runtime·atomicstore(&s->sweepgen, h->sweepgen);  	s->state = MSpanInUse;  	MHeap_FreeLocked(h, s);  	return true; @@ -273,8 +351,7 @@ runtime·MHeap_Lookup(MHeap *h, void *v)  	uintptr p;  	p = (uintptr)v; -	if(sizeof(void*) == 8) -		p -= (uintptr)h->arena_start; +	p -= (uintptr)h->arena_start;  	return h->spans[p >> PageShift];  } @@ -295,8 +372,7 @@ runtime·MHeap_LookupMaybe(MHeap *h, void *v)  		return nil;  	p = (uintptr)v>>PageShift;  	q = p; -	if(sizeof(void*) == 8) -		q -= (uintptr)h->arena_start >> PageShift; +	q -= (uintptr)h->arena_start >> PageShift;  	s = h->spans[q];  	if(s == nil || p < s->start || v >= s->limit || s->state != MSpanInUse)  		return nil; @@ -322,20 +398,19 @@ runtime·MHeap_Free(MHeap *h, MSpan *s, int32 acct)  static void  MHeap_FreeLocked(MHeap *h, MSpan *s)  { -	uintptr *sp, *tp;  	MSpan *t;  	PageID p;  	s->types.compression = MTypes_Empty; -	if(s->state != MSpanInUse || s->ref != 0) { -		runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d\n", s, s->start<<PageShift, s->state, s->ref); +	if(s->state != MSpanInUse || s->ref != 0 || s->sweepgen != h->sweepgen) { +		runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %d sweepgen %d/%d\n", +			s, s->start<<PageShift, s->state, s->ref, s->sweepgen, h->sweepgen);  		runtime·throw("MHeap_FreeLocked - invalid free");  	}  	mstats.heap_idle += s->npages<<PageShift;  	s->state = MSpanFree;  	runtime·MSpanList_Remove(s); -	sp = (uintptr*)(s->start<<PageShift);  	// Stamp newly unused spans. The scavenger will use that  	// info to potentially give back some pages to the OS.  	s->unusedsince = runtime·nanotime(); @@ -343,16 +418,12 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)  	// Coalesce with earlier, later spans.  	p = s->start; -	if(sizeof(void*) == 8) -		p -= (uintptr)h->arena_start >> PageShift; +	p -= (uintptr)h->arena_start >> PageShift;  	if(p > 0 && (t = h->spans[p-1]) != nil && t->state != MSpanInUse) { -		if(t->npreleased == 0) {  // cant't touch this otherwise -			tp = (uintptr*)(t->start<<PageShift); -			*tp |= *sp;	// propagate "needs zeroing" mark -		}  		s->start = t->start;  		s->npages += t->npages;  		s->npreleased = t->npreleased; // absorb released pages +		s->needzero |= t->needzero;  		p -= t->npages;  		h->spans[p] = s;  		runtime·MSpanList_Remove(t); @@ -360,12 +431,9 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)  		runtime·FixAlloc_Free(&h->spanalloc, t);  	}  	if((p+s->npages)*sizeof(h->spans[0]) < h->spans_mapped && (t = h->spans[p+s->npages]) != nil && t->state != MSpanInUse) { -		if(t->npreleased == 0) {  // cant't touch this otherwise -			tp = (uintptr*)(t->start<<PageShift); -			*sp |= *tp;	// propagate "needs zeroing" mark -		}  		s->npages += t->npages;  		s->npreleased += t->npreleased; +		s->needzero |= t->needzero;  		h->spans[p + s->npages - 1] = s;  		runtime·MSpanList_Remove(t);  		t->state = MSpanDead; @@ -376,7 +444,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)  	if(s->npages < nelem(h->free))  		runtime·MSpanList_Insert(&h->free[s->npages], s);  	else -		runtime·MSpanList_Insert(&h->large, s); +		runtime·MSpanList_Insert(&h->freelarge, s);  }  static void @@ -419,7 +487,7 @@ scavenge(int32 k, uint64 now, uint64 limit)  	sumreleased = 0;  	for(i=0; i < nelem(h->free); i++)  		sumreleased += scavengelist(&h->free[i], now, limit); -	sumreleased += scavengelist(&h->large, now, limit); +	sumreleased += scavengelist(&h->freelarge, now, limit);  	if(runtime·debug.gctrace > 0) {  		if(sumreleased > 0) @@ -440,6 +508,7 @@ runtime·MHeap_Scavenger(void)  {  	MHeap *h;  	uint64 tick, now, forcegc, limit; +	int64 unixnow;  	int32 k;  	Note note, *notep; @@ -463,8 +532,8 @@ runtime·MHeap_Scavenger(void)  		runtime·notetsleepg(¬e, tick);  		runtime·lock(h); -		now = runtime·nanotime(); -		if(now - mstats.last_gc > forcegc) { +		unixnow = runtime·unixnanotime(); +		if(unixnow - mstats.last_gc > forcegc) {  			runtime·unlock(h);  			// The scavenger can not block other goroutines,  			// otherwise deadlock detector can fire spuriously. @@ -476,8 +545,8 @@ runtime·MHeap_Scavenger(void)  			if(runtime·debug.gctrace > 0)  				runtime·printf("scvg%d: GC forced\n", k);  			runtime·lock(h); -			now = runtime·nanotime();  		} +		now = runtime·nanotime();  		scavenge(k, now, limit);  		runtime·unlock(h);  	} @@ -486,7 +555,7 @@ runtime·MHeap_Scavenger(void)  void  runtime∕debug·freeOSMemory(void)  { -	runtime·gc(1); +	runtime·gc(2);  // force GC and do eager sweep  	runtime·lock(&runtime·mheap);  	scavenge(-1, ~(uintptr)0, 0);  	runtime·unlock(&runtime·mheap); @@ -503,11 +572,16 @@ runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages)  	span->freelist = nil;  	span->ref = 0;  	span->sizeclass = 0; +	span->incache = false;  	span->elemsize = 0; -	span->state = 0; +	span->state = MSpanDead;  	span->unusedsince = 0;  	span->npreleased = 0;  	span->types.compression = MTypes_Empty; +	span->specialLock.key = 0; +	span->specials = nil; +	span->needzero = 0; +	span->freebuf = nil;  }  // Initialize an empty doubly-linked list. @@ -549,4 +623,310 @@ runtime·MSpanList_Insert(MSpan *list, MSpan *span)  	span->prev->next = span;  } +void +runtime·MSpanList_InsertBack(MSpan *list, MSpan *span) +{ +	if(span->next != nil || span->prev != nil) { +		runtime·printf("failed MSpanList_Insert %p %p %p\n", span, span->next, span->prev); +		runtime·throw("MSpanList_Insert"); +	} +	span->next = list; +	span->prev = list->prev; +	span->next->prev = span; +	span->prev->next = span; +} +// Adds the special record s to the list of special records for +// the object p.  All fields of s should be filled in except for +// offset & next, which this routine will fill in. +// Returns true if the special was successfully added, false otherwise. +// (The add will fail only if a record with the same p and s->kind +//  already exists.) +static bool +addspecial(void *p, Special *s) +{ +	MSpan *span; +	Special **t, *x; +	uintptr offset; +	byte kind; + +	span = runtime·MHeap_LookupMaybe(&runtime·mheap, p); +	if(span == nil) +		runtime·throw("addspecial on invalid pointer"); + +	// Ensure that the span is swept. +	// GC accesses specials list w/o locks. And it's just much safer. +	m->locks++; +	runtime·MSpan_EnsureSwept(span); + +	offset = (uintptr)p - (span->start << PageShift); +	kind = s->kind; + +	runtime·lock(&span->specialLock); + +	// Find splice point, check for existing record. +	t = &span->specials; +	while((x = *t) != nil) { +		if(offset == x->offset && kind == x->kind) { +			runtime·unlock(&span->specialLock); +			m->locks--; +			return false; // already exists +		} +		if(offset < x->offset || (offset == x->offset && kind < x->kind)) +			break; +		t = &x->next; +	} +	// Splice in record, fill in offset. +	s->offset = offset; +	s->next = x; +	*t = s; +	runtime·unlock(&span->specialLock); +	m->locks--; +	return true; +} + +// Removes the Special record of the given kind for the object p. +// Returns the record if the record existed, nil otherwise. +// The caller must FixAlloc_Free the result. +static Special* +removespecial(void *p, byte kind) +{ +	MSpan *span; +	Special *s, **t; +	uintptr offset; + +	span = runtime·MHeap_LookupMaybe(&runtime·mheap, p); +	if(span == nil) +		runtime·throw("removespecial on invalid pointer"); + +	// Ensure that the span is swept. +	// GC accesses specials list w/o locks. And it's just much safer. +	m->locks++; +	runtime·MSpan_EnsureSwept(span); + +	offset = (uintptr)p - (span->start << PageShift); + +	runtime·lock(&span->specialLock); +	t = &span->specials; +	while((s = *t) != nil) { +		// This function is used for finalizers only, so we don't check for +		// "interior" specials (p must be exactly equal to s->offset). +		if(offset == s->offset && kind == s->kind) { +			*t = s->next; +			runtime·unlock(&span->specialLock); +			m->locks--; +			return s; +		} +		t = &s->next; +	} +	runtime·unlock(&span->specialLock); +	m->locks--; +	return nil; +} + +// Adds a finalizer to the object p.  Returns true if it succeeded. +bool +runtime·addfinalizer(void *p, FuncVal *f, uintptr nret, Type *fint, PtrType *ot) +{ +	SpecialFinalizer *s; + +	runtime·lock(&runtime·mheap.speciallock); +	s = runtime·FixAlloc_Alloc(&runtime·mheap.specialfinalizeralloc); +	runtime·unlock(&runtime·mheap.speciallock); +	s->kind = KindSpecialFinalizer; +	s->fn = f; +	s->nret = nret; +	s->fint = fint; +	s->ot = ot; +	if(addspecial(p, s)) +		return true; + +	// There was an old finalizer +	runtime·lock(&runtime·mheap.speciallock); +	runtime·FixAlloc_Free(&runtime·mheap.specialfinalizeralloc, s); +	runtime·unlock(&runtime·mheap.speciallock); +	return false; +} + +// Removes the finalizer (if any) from the object p. +void +runtime·removefinalizer(void *p) +{ +	SpecialFinalizer *s; + +	s = (SpecialFinalizer*)removespecial(p, KindSpecialFinalizer); +	if(s == nil) +		return; // there wasn't a finalizer to remove +	runtime·lock(&runtime·mheap.speciallock); +	runtime·FixAlloc_Free(&runtime·mheap.specialfinalizeralloc, s); +	runtime·unlock(&runtime·mheap.speciallock); +} + +// Set the heap profile bucket associated with addr to b. +void +runtime·setprofilebucket(void *p, Bucket *b) +{ +	SpecialProfile *s; + +	runtime·lock(&runtime·mheap.speciallock); +	s = runtime·FixAlloc_Alloc(&runtime·mheap.specialprofilealloc); +	runtime·unlock(&runtime·mheap.speciallock); +	s->kind = KindSpecialProfile; +	s->b = b; +	if(!addspecial(p, s)) +		runtime·throw("setprofilebucket: profile already set"); +} + +// Do whatever cleanup needs to be done to deallocate s.  It has +// already been unlinked from the MSpan specials list. +// Returns true if we should keep working on deallocating p. +bool +runtime·freespecial(Special *s, void *p, uintptr size, bool freed) +{ +	SpecialFinalizer *sf; +	SpecialProfile *sp; + +	switch(s->kind) { +	case KindSpecialFinalizer: +		sf = (SpecialFinalizer*)s; +		runtime·queuefinalizer(p, sf->fn, sf->nret, sf->fint, sf->ot); +		runtime·lock(&runtime·mheap.speciallock); +		runtime·FixAlloc_Free(&runtime·mheap.specialfinalizeralloc, sf); +		runtime·unlock(&runtime·mheap.speciallock); +		return false; // don't free p until finalizer is done +	case KindSpecialProfile: +		sp = (SpecialProfile*)s; +		runtime·MProf_Free(sp->b, size, freed); +		runtime·lock(&runtime·mheap.speciallock); +		runtime·FixAlloc_Free(&runtime·mheap.specialprofilealloc, sp); +		runtime·unlock(&runtime·mheap.speciallock); +		return true; +	default: +		runtime·throw("bad special kind"); +		return true; +	} +} + +// Free all special records for p. +void +runtime·freeallspecials(MSpan *span, void *p, uintptr size) +{ +	Special *s, **t, *list; +	uintptr offset; + +	if(span->sweepgen != runtime·mheap.sweepgen) +		runtime·throw("runtime: freeallspecials: unswept span"); +	// first, collect all specials into the list; then, free them +	// this is required to not cause deadlock between span->specialLock and proflock +	list = nil; +	offset = (uintptr)p - (span->start << PageShift); +	runtime·lock(&span->specialLock); +	t = &span->specials; +	while((s = *t) != nil) { +		if(offset + size <= s->offset) +			break; +		if(offset <= s->offset) { +			*t = s->next; +			s->next = list; +			list = s; +		} else +			t = &s->next; +	} +	runtime·unlock(&span->specialLock); + +	while(list != nil) { +		s = list; +		list = s->next; +		if(!runtime·freespecial(s, p, size, true)) +			runtime·throw("can't explicitly free an object with a finalizer"); +	} +} + +// Split an allocated span into two equal parts. +void +runtime·MHeap_SplitSpan(MHeap *h, MSpan *s) +{ +	MSpan *t; +	MCentral *c; +	uintptr i; +	uintptr npages; +	PageID p; + +	if(s->state != MSpanInUse) +		runtime·throw("MHeap_SplitSpan on a free span"); +	if(s->sizeclass != 0 && s->ref != 1) +		runtime·throw("MHeap_SplitSpan doesn't have an allocated object"); +	npages = s->npages; + +	// remove the span from whatever list it is in now +	if(s->sizeclass > 0) { +		// must be in h->central[x].empty +		c = &h->central[s->sizeclass]; +		runtime·lock(c); +		runtime·MSpanList_Remove(s); +		runtime·unlock(c); +		runtime·lock(h); +	} else { +		// must be in h->busy/busylarge +		runtime·lock(h); +		runtime·MSpanList_Remove(s); +	} +	// heap is locked now + +	if(npages == 1) { +		// convert span of 1 PageSize object to a span of 2 PageSize/2 objects. +		s->ref = 2; +		s->sizeclass = runtime·SizeToClass(PageSize/2); +		s->elemsize = PageSize/2; +	} else { +		// convert span of n>1 pages into two spans of n/2 pages each. +		if((s->npages & 1) != 0) +			runtime·throw("MHeap_SplitSpan on an odd size span"); + +		// compute position in h->spans +		p = s->start; +		p -= (uintptr)h->arena_start >> PageShift; + +		// Allocate a new span for the first half. +		t = runtime·FixAlloc_Alloc(&h->spanalloc); +		runtime·MSpan_Init(t, s->start, npages/2); +		t->limit = (byte*)((t->start + npages/2) << PageShift); +		t->state = MSpanInUse; +		t->elemsize = npages << (PageShift - 1); +		t->sweepgen = s->sweepgen; +		if(t->elemsize <= MaxSmallSize) { +			t->sizeclass = runtime·SizeToClass(t->elemsize); +			t->ref = 1; +		} + +		// the old span holds the second half. +		s->start += npages/2; +		s->npages = npages/2; +		s->elemsize = npages << (PageShift - 1); +		if(s->elemsize <= MaxSmallSize) { +			s->sizeclass = runtime·SizeToClass(s->elemsize); +			s->ref = 1; +		} + +		// update span lookup table +		for(i = p; i < p + npages/2; i++) +			h->spans[i] = t; +	} + +	// place the span into a new list +	if(s->sizeclass > 0) { +		runtime·unlock(h); +		c = &h->central[s->sizeclass]; +		runtime·lock(c); +		// swept spans are at the end of the list +		runtime·MSpanList_InsertBack(&c->empty, s); +		runtime·unlock(c); +	} else { +		// Swept spans are at the end of lists. +		if(s->npages < nelem(h->free)) +			runtime·MSpanList_InsertBack(&h->busy[s->npages], s); +		else +			runtime·MSpanList_InsertBack(&h->busylarge, s); +		runtime·unlock(h); +	} +} | 
