e8c0d7ab595a74fa63e86d3a8e03e087df4fd542
[mono.git] / mono / sgen / sgen-internal.c
1 /*
2  * sgen-internal.c: Internal lock-free memory allocator.
3  *
4  * Copyright (C) 2012 Xamarin Inc
5  *
6  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7  */
8
9 #include "config.h"
10
11 #ifdef HAVE_SGEN_GC
12
13 #include <string.h>
14
15 #include "mono/sgen/sgen-gc.h"
16 #include "mono/utils/lock-free-alloc.h"
17 #include "mono/sgen/sgen-memory-governor.h"
18 #include "mono/sgen/sgen-client.h"
19
20 /*
21  * When allocating sgen memory we choose the allocator with the smallest slot size
22  * that can fit our requested size. These slots are allocated within a block that
23  * can contain at least 2 slots of the specific size.
24  *
25  * Currently, slots from 8 to 2044/2040 are allocated inside 4096 sized blocks,
26  * 2728 to 4092/4088 inside 8192 sized blocks, and higher inside 16384 sized
27  * blocks. We also need to make sure the slots are pointer size aligned so we
28  * don't allocate unaligned memory.
29  *
30  * The computation of these sizes spawns from two basic rules :
31  *      - if we use slots of size s1 that fit n times in a block, it is illogical
32  * to use another slot of size s2 which also fits the same n times in a block.
33  *      - if we use slots of size s1 that fit n times in a block, there is no
34  * s2 > s1 that can fit n times in the block. That would mean we are wasting memory
35  * when allocating size S where s1 < S <= s2.
36  */
37 #if SIZEOF_VOID_P == 4
38 static const int allocator_sizes [] = {
39            8,   16,   24,   32,   40,   48,   64,   80,
40           96,  124,  160,  192,  224,  252,  292,  340,
41          408,  452,  508,  584,  680,  816, 1020,
42         1364, 2044, 2728, 4092, 5460, 8188 };
43 #else
44 static const int allocator_sizes [] = {
45            8,   16,   24,   32,   40,   48,   64,   80,
46           96,  128,  160,  192,  224,  248,  288,  336,
47          368,  448,  504,  584,  680,  816, 1016,
48         1360, 2040, 2728, 4088, 5456, 8184 };
49 #endif
50
51 #define NUM_ALLOCATORS  (sizeof (allocator_sizes) / sizeof (int))
52
53 static int allocator_block_sizes [NUM_ALLOCATORS];
54
55 static MonoLockFreeAllocSizeClass size_classes [NUM_ALLOCATORS];
56 static MonoLockFreeAllocator allocators [NUM_ALLOCATORS];
57
58 #ifdef HEAVY_STATISTICS
59 static int allocator_sizes_stats [NUM_ALLOCATORS];
60 #endif
61
62 static size_t
63 block_size (size_t slot_size)
64 {
65         static int pagesize = -1;
66
67         int size;
68         size_t aligned_slot_size = SGEN_ALIGN_UP_TO (slot_size, SIZEOF_VOID_P);
69
70         if (pagesize == -1)
71                 pagesize = mono_pagesize ();
72
73         for (size = pagesize; size < LOCK_FREE_ALLOC_SB_MAX_SIZE; size <<= 1) {
74                 if (aligned_slot_size * 2 <= LOCK_FREE_ALLOC_SB_USABLE_SIZE (size))
75                         return size;
76         }
77         return LOCK_FREE_ALLOC_SB_MAX_SIZE;
78 }
79
80 /*
81  * Find the allocator index for memory chunks that can contain @size
82  * objects.
83  */
84 static int
85 index_for_size (size_t size)
86 {
87         int slot;
88         /* do a binary search or lookup table later. */
89         for (slot = 0; slot < NUM_ALLOCATORS; ++slot) {
90                 if (allocator_sizes [slot] >= size)
91                         return slot;
92         }
93         g_assert_not_reached ();
94         return -1;
95 }
96
97 /*
98  * Allocator indexes for the fixed INTERNAL_MEM_XXX types.  -1 if that
99  * type is dynamic.
100  */
101 static int fixed_type_allocator_indexes [INTERNAL_MEM_MAX];
102
103 void
104 sgen_register_fixed_internal_mem_type (int type, size_t size)
105 {
106         int slot;
107
108         g_assert (type >= 0 && type < INTERNAL_MEM_MAX);
109         g_assert (size <= allocator_sizes [NUM_ALLOCATORS - 1]);
110
111         slot = index_for_size (size);
112         g_assert (slot >= 0);
113
114         if (fixed_type_allocator_indexes [type] == -1)
115                 fixed_type_allocator_indexes [type] = slot;
116         else {
117                 if (fixed_type_allocator_indexes [type] != slot)
118                         g_error ("Invalid double registration of type %d old slot %d new slot %d", type, fixed_type_allocator_indexes [type], slot);
119         }
120 }
121
122 static const char*
123 description_for_type (int type)
124 {
125         switch (type) {
126         case INTERNAL_MEM_PIN_QUEUE: return "pin-queue";
127         case INTERNAL_MEM_FRAGMENT: return "fragment";
128         case INTERNAL_MEM_SECTION: return "section";
129         case INTERNAL_MEM_SCAN_STARTS: return "scan-starts";
130         case INTERNAL_MEM_FIN_TABLE: return "fin-table";
131         case INTERNAL_MEM_FINALIZE_ENTRY: return "finalize-entry";
132         case INTERNAL_MEM_FINALIZE_READY: return "finalize-ready";
133         case INTERNAL_MEM_DISLINK_TABLE: return "dislink-table";
134         case INTERNAL_MEM_DISLINK: return "dislink";
135         case INTERNAL_MEM_ROOTS_TABLE: return "roots-table";
136         case INTERNAL_MEM_ROOT_RECORD: return "root-record";
137         case INTERNAL_MEM_STATISTICS: return "statistics";
138         case INTERNAL_MEM_STAT_PINNED_CLASS: return "pinned-class";
139         case INTERNAL_MEM_STAT_REMSET_CLASS: return "remset-class";
140         case INTERNAL_MEM_GRAY_QUEUE: return "gray-queue";
141         case INTERNAL_MEM_MS_TABLES: return "marksweep-tables";
142         case INTERNAL_MEM_MS_BLOCK_INFO: return "marksweep-block-info";
143         case INTERNAL_MEM_MS_BLOCK_INFO_SORT: return "marksweep-block-info-sort";
144         case INTERNAL_MEM_WORKER_DATA: return "worker-data";
145         case INTERNAL_MEM_THREAD_POOL_JOB: return "thread-pool-job";
146         case INTERNAL_MEM_BRIDGE_DATA: return "bridge-data";
147         case INTERNAL_MEM_OLD_BRIDGE_HASH_TABLE: return "old-bridge-hash-table";
148         case INTERNAL_MEM_OLD_BRIDGE_HASH_TABLE_ENTRY: return "old-bridge-hash-table-entry";
149         case INTERNAL_MEM_BRIDGE_HASH_TABLE: return "bridge-hash-table";
150         case INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY: return "bridge-hash-table-entry";
151         case INTERNAL_MEM_TARJAN_BRIDGE_HASH_TABLE: return "tarjan-bridge-hash-table";
152         case INTERNAL_MEM_TARJAN_BRIDGE_HASH_TABLE_ENTRY: return "tarjan-bridge-hash-table-entry";
153         case INTERNAL_MEM_TARJAN_OBJ_BUCKET: return "tarjan-bridge-object-buckets";
154         case INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE: return "bridge-alive-hash-table";
155         case INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE_ENTRY: return "bridge-alive-hash-table-entry";
156         case INTERNAL_MEM_BRIDGE_DEBUG: return "bridge-debug";
157         case INTERNAL_MEM_TOGGLEREF_DATA: return "toggleref-data";
158         case INTERNAL_MEM_CARDTABLE_MOD_UNION: return "cardtable-mod-union";
159         case INTERNAL_MEM_BINARY_PROTOCOL: return "binary-protocol";
160         case INTERNAL_MEM_TEMPORARY: return "temporary";
161         case INTERNAL_MEM_LOG_ENTRY: return "log-entry";
162         case INTERNAL_MEM_COMPLEX_DESCRIPTORS: return "complex-descriptors";
163         default: {
164                 const char *description = sgen_client_description_for_internal_mem_type (type);
165                 SGEN_ASSERT (0, description, "Unknown internal mem type");
166                 return description;
167         }
168         }
169 }
170
171 void*
172 sgen_alloc_internal_dynamic (size_t size, int type, gboolean assert_on_failure)
173 {
174         int index;
175         void *p;
176
177         if (size > allocator_sizes [NUM_ALLOCATORS - 1]) {
178                 p = sgen_alloc_os_memory (size, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), NULL, MONO_MEM_ACCOUNT_SGEN_INTERNAL);
179                 if (!p)
180                         sgen_assert_memory_alloc (NULL, size, description_for_type (type));
181         } else {
182                 index = index_for_size (size);
183
184 #ifdef HEAVY_STATISTICS
185                 ++ allocator_sizes_stats [index];
186 #endif
187
188                 p = mono_lock_free_alloc (&allocators [index]);
189                 if (!p)
190                         sgen_assert_memory_alloc (NULL, size, description_for_type (type));
191                 memset (p, 0, size);
192         }
193
194         SGEN_ASSERT (0, !(((mword)p) & (sizeof(gpointer) - 1)), "Why do we allocate unaligned addresses ?");
195         return p;
196 }
197
198 void
199 sgen_free_internal_dynamic (void *addr, size_t size, int type)
200 {
201         if (!addr)
202                 return;
203
204         if (size > allocator_sizes [NUM_ALLOCATORS - 1])
205                 sgen_free_os_memory (addr, size, SGEN_ALLOC_INTERNAL, MONO_MEM_ACCOUNT_SGEN_INTERNAL);
206         else
207                 mono_lock_free_free (addr, block_size (size));
208 }
209
210 void*
211 sgen_alloc_internal (int type)
212 {
213         int index, size;
214         void *p;
215
216         index = fixed_type_allocator_indexes [type];
217         g_assert (index >= 0 && index < NUM_ALLOCATORS);
218
219 #ifdef HEAVY_STATISTICS
220         ++ allocator_sizes_stats [index];
221 #endif
222
223         size = allocator_sizes [index];
224
225         p = mono_lock_free_alloc (&allocators [index]);
226         memset (p, 0, size);
227
228         SGEN_ASSERT (0, !(((mword)p) & (sizeof(gpointer) - 1)), "Why do we allocate unaligned addresses ?");
229
230         return p;
231 }
232
233 void
234 sgen_free_internal (void *addr, int type)
235 {
236         int index;
237
238         if (!addr)
239                 return;
240
241         index = fixed_type_allocator_indexes [type];
242         g_assert (index >= 0 && index < NUM_ALLOCATORS);
243
244         mono_lock_free_free (addr, allocator_block_sizes [index]);
245 }
246
247 void
248 sgen_dump_internal_mem_usage (FILE *heap_dump_file)
249 {
250         /*
251         int i;
252
253         fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%lld\"/>\n", large_internal_bytes_alloced);
254         fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%lld\"/>\n", pinned_chunk_bytes_alloced);
255         for (i = 0; i < INTERNAL_MEM_MAX; ++i) {
256                 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n",
257                                 description_for_type (i), unmanaged_allocator.small_internal_mem_bytes [i]);
258         }
259         */
260 }
261
262 void
263 sgen_report_internal_mem_usage (void)
264 {
265         int i G_GNUC_UNUSED;
266 #ifdef HEAVY_STATISTICS
267         printf ("size -> # allocations\n");
268         for (i = 0; i < NUM_ALLOCATORS; ++i)
269                 printf ("%d -> %d\n", allocator_sizes [i], allocator_sizes_stats [i]);
270 #endif
271 }
272
273 void
274 sgen_init_internal_allocator (void)
275 {
276         int i, size;
277
278         for (i = 0; i < INTERNAL_MEM_MAX; ++i)
279                 fixed_type_allocator_indexes [i] = -1;
280
281         for (i = 0; i < NUM_ALLOCATORS; ++i) {
282                 allocator_block_sizes [i] = block_size (allocator_sizes [i]);
283                 mono_lock_free_allocator_init_size_class (&size_classes [i], allocator_sizes [i], allocator_block_sizes [i]);
284                 mono_lock_free_allocator_init_allocator (&allocators [i], &size_classes [i], MONO_MEM_ACCOUNT_SGEN_INTERNAL);
285         }
286
287         for (size = mono_pagesize (); size <= LOCK_FREE_ALLOC_SB_MAX_SIZE; size <<= 1) {
288                 int max_size = (LOCK_FREE_ALLOC_SB_USABLE_SIZE (size) / 2) & ~(SIZEOF_VOID_P - 1);
289                 /*
290                  * we assert that allocator_sizes contains the biggest possible object size
291                  * per block which has to be an aligned address.
292                  * (4K => 2040, 8k => 4088, 16k => 8184 on 64bits),
293                  * so that we do not get different block sizes for sizes that should go to the same one
294                  */
295                 g_assert (allocator_sizes [index_for_size (max_size)] == max_size);
296                 g_assert (block_size (max_size) == size);
297                 if (size < LOCK_FREE_ALLOC_SB_MAX_SIZE)
298                         g_assert (block_size (max_size + 1) == size << 1);
299         }
300 }
301
302 #endif