Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // Central free lists. |
| 6 | // |
| 7 | // See malloc.h for an overview. |
| 8 | // |
| 9 | // The MCentral doesn't actually contain the list of free objects; the MSpan does. |
| 10 | // Each MCentral is two lists of MSpans: those with free objects (c->nonempty) |
| 11 | // and those that are completely allocated (c->empty). |
| 12 | // |
| 13 | // TODO(rsc): tcmalloc uses a "transfer cache" to split the list |
| 14 | // into sections of class_to_transfercount[sizeclass] objects |
| 15 | // so that it is faster to move those lists between MCaches and MCentrals. |
| 16 | |
| 17 | #include "runtime.h" |
Russ Cox | 851f301 | 2011-12-16 15:33:58 -0500 | [diff] [blame] | 18 | #include "arch_GOARCH.h" |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 19 | #include "malloc.h" |
| 20 | |
| 21 | static bool MCentral_Grow(MCentral *c); |
| 22 | static void* MCentral_Alloc(MCentral *c); |
| 23 | static void MCentral_Free(MCentral *c, void *v); |
| 24 | |
| 25 | // Initialize a single central free list. |
| 26 | void |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 27 | runtime·MCentral_Init(MCentral *c, int32 sizeclass) |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 28 | { |
| 29 | c->sizeclass = sizeclass; |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 30 | runtime·MSpanList_Init(&c->nonempty); |
| 31 | runtime·MSpanList_Init(&c->empty); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | // Allocate up to n objects from the central free list. |
| 35 | // Return the number of objects allocated. |
| 36 | // The objects are linked together by their first words. |
| 37 | // On return, *pstart points at the first object and *pend at the last. |
| 38 | int32 |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 39 | runtime·MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 40 | { |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 41 | MLink *first, *last, *v; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 42 | int32 i; |
| 43 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 44 | runtime·lock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 45 | // Replenish central list if empty. |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 46 | if(runtime·MSpanList_IsEmpty(&c->nonempty)) { |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 47 | if(!MCentral_Grow(c)) { |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 48 | runtime·unlock(c); |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 49 | *pfirst = nil; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 50 | return 0; |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | // Copy from list, up to n. |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 55 | // First one is guaranteed to work, because we just grew the list. |
| 56 | first = MCentral_Alloc(c); |
| 57 | last = first; |
| 58 | for(i=1; i<n && (v = MCentral_Alloc(c)) != nil; i++) { |
| 59 | last->next = v; |
| 60 | last = v; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 61 | } |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 62 | last->next = nil; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 63 | c->nfree -= i; |
| 64 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 65 | runtime·unlock(c); |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 66 | *pfirst = first; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 67 | return i; |
| 68 | } |
| 69 | |
| 70 | // Helper: allocate one object from the central free list. |
| 71 | static void* |
| 72 | MCentral_Alloc(MCentral *c) |
| 73 | { |
| 74 | MSpan *s; |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 75 | MLink *v; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 76 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 77 | if(runtime·MSpanList_IsEmpty(&c->nonempty)) |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 78 | return nil; |
| 79 | s = c->nonempty.next; |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 80 | s->ref++; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 81 | v = s->freelist; |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 82 | s->freelist = v->next; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 83 | if(s->freelist == nil) { |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 84 | runtime·MSpanList_Remove(s); |
| 85 | runtime·MSpanList_Insert(&c->empty, s); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 86 | } |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 87 | return v; |
| 88 | } |
| 89 | |
| 90 | // Free n objects back into the central free list. |
| 91 | // Return the number of objects allocated. |
| 92 | // The objects are linked together by their first words. |
| 93 | // On return, *pstart points at the first object and *pend at the last. |
| 94 | void |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 95 | runtime·MCentral_FreeList(MCentral *c, int32 n, MLink *start) |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 96 | { |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 97 | MLink *v, *next; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 98 | |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 99 | // Assume next == nil marks end of list. |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 100 | // n and end would be useful if we implemented |
| 101 | // the transfer cache optimization in the TODO above. |
| 102 | USED(n); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 103 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 104 | runtime·lock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 105 | for(v=start; v; v=next) { |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 106 | next = v->next; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 107 | MCentral_Free(c, v); |
| 108 | } |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 109 | runtime·unlock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | // Helper: free one object back into the central free list. |
| 113 | static void |
| 114 | MCentral_Free(MCentral *c, void *v) |
| 115 | { |
| 116 | MSpan *s; |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 117 | MLink *p; |
Russ Cox | f25586a | 2010-02-10 00:00:12 -0800 | [diff] [blame] | 118 | int32 size; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 119 | |
| 120 | // Find span for v. |
Russ Cox | 4608feb | 2011-01-28 15:03:26 -0500 | [diff] [blame] | 121 | s = runtime·MHeap_Lookup(&runtime·mheap, v); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 122 | if(s == nil || s->ref == 0) |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 123 | runtime·throw("invalid free"); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 124 | |
| 125 | // Move to nonempty if necessary. |
| 126 | if(s->freelist == nil) { |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 127 | runtime·MSpanList_Remove(s); |
| 128 | runtime·MSpanList_Insert(&c->nonempty, s); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | // Add v back to s's free list. |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 132 | p = v; |
| 133 | p->next = s->freelist; |
| 134 | s->freelist = p; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 135 | c->nfree++; |
| 136 | |
| 137 | // If s is completely freed, return it to the heap. |
| 138 | if(--s->ref == 0) { |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 139 | size = runtime·class_to_size[c->sizeclass]; |
| 140 | runtime·MSpanList_Remove(s); |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 141 | runtime·unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift); |
| 142 | *(uintptr*)(s->start<<PageShift) = 1; // needs zeroing |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 143 | s->freelist = nil; |
Russ Cox | f25586a | 2010-02-10 00:00:12 -0800 | [diff] [blame] | 144 | c->nfree -= (s->npages << PageShift) / size; |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 145 | runtime·unlock(c); |
| 146 | runtime·MHeap_Free(&runtime·mheap, s, 0); |
| 147 | runtime·lock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 148 | } |
| 149 | } |
| 150 | |
Russ Cox | fb94be5 | 2010-02-10 14:59:39 -0800 | [diff] [blame] | 151 | void |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 152 | runtime·MGetSizeClassInfo(int32 sizeclass, uintptr *sizep, int32 *npagesp, int32 *nobj) |
Russ Cox | fb94be5 | 2010-02-10 14:59:39 -0800 | [diff] [blame] | 153 | { |
| 154 | int32 size; |
| 155 | int32 npages; |
| 156 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 157 | npages = runtime·class_to_allocnpages[sizeclass]; |
| 158 | size = runtime·class_to_size[sizeclass]; |
Russ Cox | fb94be5 | 2010-02-10 14:59:39 -0800 | [diff] [blame] | 159 | *npagesp = npages; |
| 160 | *sizep = size; |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 161 | *nobj = (npages << PageShift) / size; |
Russ Cox | fb94be5 | 2010-02-10 14:59:39 -0800 | [diff] [blame] | 162 | } |
| 163 | |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 164 | // Fetch a new span from the heap and |
| 165 | // carve into objects for the free list. |
| 166 | static bool |
| 167 | MCentral_Grow(MCentral *c) |
| 168 | { |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 169 | int32 i, n, npages; |
| 170 | uintptr size; |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 171 | MLink **tailp, *v; |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 172 | byte *p; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 173 | MSpan *s; |
| 174 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 175 | runtime·unlock(c); |
| 176 | runtime·MGetSizeClassInfo(c->sizeclass, &size, &npages, &n); |
| 177 | s = runtime·MHeap_Alloc(&runtime·mheap, npages, c->sizeclass, 0); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 178 | if(s == nil) { |
| 179 | // TODO(rsc): Log out of memory |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 180 | runtime·lock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 181 | return false; |
| 182 | } |
| 183 | |
| 184 | // Carve span into sequence of blocks. |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 185 | tailp = &s->freelist; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 186 | p = (byte*)(s->start << PageShift); |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 187 | s->limit = p + size*n; |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 188 | for(i=0; i<n; i++) { |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 189 | v = (MLink*)p; |
| 190 | *tailp = v; |
| 191 | tailp = &v->next; |
Russ Cox | 1ce1791 | 2009-01-26 17:37:05 -0800 | [diff] [blame] | 192 | p += size; |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 193 | } |
Russ Cox | da0a7d7 | 2008-12-19 03:13:39 -0800 | [diff] [blame] | 194 | *tailp = nil; |
Russ Cox | 1e063b3 | 2011-02-02 23:03:47 -0500 | [diff] [blame] | 195 | runtime·markspan((byte*)(s->start<<PageShift), size, n, size*n < (s->npages<<PageShift)); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 196 | |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 197 | runtime·lock(c); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 198 | c->nfree += n; |
Russ Cox | 68b4255 | 2010-11-04 14:00:19 -0400 | [diff] [blame] | 199 | runtime·MSpanList_Insert(&c->nonempty, s); |
Russ Cox | e29ce17 | 2008-12-18 15:42:28 -0800 | [diff] [blame] | 200 | return true; |
| 201 | } |