summaryrefslogtreecommitdiff
path: root/libgo/runtime/mcache.c
blob: 746711a0d3bdba2adcce4077a3512350a4cfa1a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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.

// Per-P malloc cache for small objects.
//
// See malloc.h for an overview.

#include "runtime.h"
#include "arch.h"
#include "malloc.h"

extern volatile intgo runtime_MemProfileRate
  __asm__ (GOSYM_PREFIX "runtime.MemProfileRate");

// dummy MSpan that contains no free objects.
static MSpan emptymspan;

MCache*
runtime_allocmcache(void)
{
	intgo rate;
	MCache *c;
	int32 i;

	runtime_lock(&runtime_mheap);
	c = runtime_FixAlloc_Alloc(&runtime_mheap.cachealloc);
	runtime_unlock(&runtime_mheap);
	runtime_memclr((byte*)c, sizeof(*c));
	for(i = 0; i < NumSizeClasses; i++)
		c->alloc[i] = &emptymspan;

	// Set first allocation sample size.
	rate = runtime_MemProfileRate;
	if(rate > 0x3fffffff)	// make 2*rate not overflow
		rate = 0x3fffffff;
	if(rate != 0)
		c->next_sample = runtime_fastrand1() % (2*rate);

	return c;
}

void
runtime_freemcache(MCache *c)
{
	runtime_MCache_ReleaseAll(c);
	runtime_lock(&runtime_mheap);
	runtime_purgecachedstats(c);
	runtime_FixAlloc_Free(&runtime_mheap.cachealloc, c);
	runtime_unlock(&runtime_mheap);
}

// Gets a span that has a free object in it and assigns it
// to be the cached span for the given sizeclass.  Returns this span.
MSpan*
runtime_MCache_Refill(MCache *c, int32 sizeclass)
{
	MCacheList *l;
	MSpan *s;

	runtime_m()->locks++;
	// Return the current cached span to the central lists.
	s = c->alloc[sizeclass];
	if(s->freelist != nil)
		runtime_throw("refill on a nonempty span");
	if(s != &emptymspan)
		runtime_MCentral_UncacheSpan(&runtime_mheap.central[sizeclass], s);

	// Push any explicitly freed objects to the central lists.
	// Not required, but it seems like a good time to do it.
	l = &c->free[sizeclass];
	if(l->nlist > 0) {
		runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], l->list);
		l->list = nil;
		l->nlist = 0;
	}

	// Get a new cached span from the central lists.
	s = runtime_MCentral_CacheSpan(&runtime_mheap.central[sizeclass]);
	if(s == nil)
		runtime_throw("out of memory");
	if(s->freelist == nil) {
		runtime_printf("%d %d\n", s->ref, (int32)((s->npages << PageShift) / s->elemsize));
		runtime_throw("empty span");
	}
	c->alloc[sizeclass] = s;
	runtime_m()->locks--;
	return s;
}

void
runtime_MCache_Free(MCache *c, MLink *p, int32 sizeclass, uintptr size)
{
	MCacheList *l;

	// Put on free list.
	l = &c->free[sizeclass];
	p->next = l->list;
	l->list = p;
	l->nlist++;

	// We transfer a span at a time from MCentral to MCache,
	// so we'll do the same in the other direction.
	if(l->nlist >= (runtime_class_to_allocnpages[sizeclass]<<PageShift)/size) {
		runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], l->list);
		l->list = nil;
		l->nlist = 0;
	}
}

void
runtime_MCache_ReleaseAll(MCache *c)
{
	int32 i;
	MSpan *s;
	MCacheList *l;

	for(i=0; i<NumSizeClasses; i++) {
		s = c->alloc[i];
		if(s != &emptymspan) {
			runtime_MCentral_UncacheSpan(&runtime_mheap.central[i], s);
			c->alloc[i] = &emptymspan;
		}
		l = &c->free[i];
		if(l->nlist > 0) {
			runtime_MCentral_FreeList(&runtime_mheap.central[i], l->list);
			l->list = nil;
			l->nlist = 0;
		}
	}
}