Merge pull request #1319 from directhex/systemwide-per-arch-aot-cache
[mono.git] / mono / metadata / mempool.c
1 /*
2  * mempool.c: efficient memory allocation
3  *
4  * MonoMemPool is for fast allocation of memory. We free
5  * all memory when the pool is destroyed.
6  *
7  * Author:
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *
10  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
11  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
12  * Copyright 2011 Xamarin Inc. (http://www.xamarin.com)
13  */
14
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18
19 #include "mempool.h"
20 #include "mempool-internals.h"
21
22 #if USE_MALLOC_FOR_MEMPOOLS
23 #define MALLOC_ALLOCATION
24 #endif
25
26 /*
27  * MonoMemPool is for fast allocation of memory. We free
28  * all memory when the pool is destroyed.
29  */
30
31 #define MEM_ALIGN 8
32 #define ALIGN_SIZE(s)   (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1))
33 #define SIZEOF_MEM_POOL (ALIGN_SIZE (sizeof (MonoMemPool)))
34
35 #if MONO_SMALL_CONFIG
36 #define MONO_MEMPOOL_PAGESIZE 4096
37 #define MONO_MEMPOOL_MINSIZE 256
38 #else
39 #define MONO_MEMPOOL_PAGESIZE 8192
40 #define MONO_MEMPOOL_MINSIZE 512
41 #endif
42
43 #ifndef G_LIKELY
44 #define G_LIKELY(a) (a)
45 #define G_UNLIKELY(a) (a)
46 #endif
47
48 #ifdef MALLOC_ALLOCATION
49 typedef struct _Chunk {
50         struct _Chunk *next;
51         guint32 size;
52 } Chunk;
53
54 struct _MonoMemPool {
55         Chunk *chunks;
56         guint32 allocated;
57 };
58 #else
59 struct _MonoMemPool {
60         MonoMemPool *next;
61         gint rest;
62         guint8 *pos, *end;
63         guint32 size;
64         union {
65                 double pad; /* to assure proper alignment */
66                 guint32 allocated;
67         } d;
68 };
69 #endif
70
71 static long total_bytes_allocated = 0;
72
73 /**
74  * mono_mempool_new:
75  *
76  * Returns: a new memory pool.
77  */
78 MonoMemPool *
79 mono_mempool_new (void)
80 {
81         return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
82 }
83
84 MonoMemPool *
85 mono_mempool_new_size (int initial_size)
86 {
87 #ifdef MALLOC_ALLOCATION
88         return g_new0 (MonoMemPool, 1);
89 #else
90         MonoMemPool *pool;
91         if (initial_size < MONO_MEMPOOL_MINSIZE)
92                 initial_size = MONO_MEMPOOL_MINSIZE;
93         pool = g_malloc (initial_size);
94
95         pool->next = NULL;
96         pool->pos = (guint8*)pool + SIZEOF_MEM_POOL;
97         pool->end = pool->pos + initial_size - SIZEOF_MEM_POOL;
98         pool->d.allocated = pool->size = initial_size;
99         total_bytes_allocated += initial_size;
100         return pool;
101 #endif
102 }
103
104 /**
105  * mono_mempool_destroy:
106  * @pool: the memory pool to destroy
107  *
108  * Free all memory associated with this pool.
109  */
110 void
111 mono_mempool_destroy (MonoMemPool *pool)
112 {
113 #ifdef MALLOC_ALLOCATION
114         mono_mempool_empty (pool);
115
116         g_free (pool);
117 #else
118         MonoMemPool *p, *n;
119
120         total_bytes_allocated -= pool->d.allocated;
121
122         p = pool;
123         while (p) {
124                 n = p->next;
125                 g_free (p);
126                 p = n;
127         }
128 #endif
129 }
130
131 /**
132  * mono_mempool_invalidate:
133  * @pool: the memory pool to invalidate
134  *
135  * Fill the memory associated with this pool to 0x2a (42). Useful for debugging.
136  */
137 void
138 mono_mempool_invalidate (MonoMemPool *pool)
139 {
140 #ifdef MALLOC_ALLOCATION
141         g_assert_not_reached ();
142 #else
143         MonoMemPool *p, *n;
144
145         p = pool;
146         while (p) {
147                 n = p->next;
148                 memset (p, 42, p->size);
149                 p = n;
150         }
151 #endif
152 }
153
154 void
155 mono_mempool_empty (MonoMemPool *pool)
156 {
157 #ifdef MALLOC_ALLOCATION
158         Chunk *p, *n;
159
160         p = pool->chunks;
161         pool->chunks = NULL;
162         while (p) {
163                 n = p->next;
164                 g_free (p);
165                 p = n;
166         }
167
168         pool->allocated = 0;
169 #else
170         pool->pos = (guint8*)pool + SIZEOF_MEM_POOL;
171         pool->end = pool->pos + pool->size - SIZEOF_MEM_POOL;
172 #endif
173 }
174
175 /**
176  * mono_mempool_stats:
177  * @pool: the momory pool we need stats for
178  *
179  * Print a few stats about the mempool
180  */
181 void
182 mono_mempool_stats (MonoMemPool *pool)
183 {
184 #ifdef MALLOC_ALLOCATION
185         g_assert_not_reached ();
186 #else
187         MonoMemPool *p;
188         int count = 0;
189         guint32 still_free = 0;
190
191         p = pool;
192         while (p) {
193                 still_free += p->end - p->pos;
194                 p = p->next;
195                 count++;
196         }
197         if (pool) {
198                 g_print ("Mempool %p stats:\n", pool);
199                 g_print ("Total mem allocated: %d\n", pool->d.allocated);
200                 g_print ("Num chunks: %d\n", count);
201                 g_print ("Free memory: %d\n", still_free);
202         }
203 #endif
204 }
205
206 #ifndef MALLOC_ALLOCATION
207 #ifdef TRACE_ALLOCATIONS
208 #include <execinfo.h>
209 #include "metadata/appdomain.h"
210 #include "metadata/metadata-internals.h"
211
212 static mono_mutex_t mempool_tracing_lock;
213 #define BACKTRACE_DEPTH 7
214 static void
215 mono_backtrace (int size)
216 {
217         void *array[BACKTRACE_DEPTH];
218         char **names;
219         int i, symbols;
220         static gboolean inited;
221
222         if (!inited) {
223             mono_mutex_init_recursive (&mempool_tracing_lock);
224             inited = TRUE;
225         }
226
227         mono_mutex_lock (&mempool_tracing_lock);
228         g_print ("Allocating %d bytes\n", size);
229         symbols = backtrace (array, BACKTRACE_DEPTH);
230         names = backtrace_symbols (array, symbols);
231         for (i = 1; i < symbols; ++i) {
232                 g_print ("\t%s\n", names [i]);
233         }
234         free (names);
235         mono_mutex_unlock (&mempool_tracing_lock);
236 }
237
238 #endif
239
240 static int
241 get_next_size (MonoMemPool *pool, int size)
242 {
243         int target = pool->next? pool->next->size: pool->size;
244         size += SIZEOF_MEM_POOL;
245         /* increase the size */
246         target += target / 2;
247         while (target < size) {
248                 target += target / 2;
249         }
250         if (target > MONO_MEMPOOL_PAGESIZE && size <= MONO_MEMPOOL_PAGESIZE)
251                 target = MONO_MEMPOOL_PAGESIZE;
252         return target;
253 }
254 #endif
255
256 /**
257  * mono_mempool_alloc:
258  * @pool: the momory pool to use
259  * @size: size of the momory block
260  *
261  * Allocates a new block of memory in @pool.
262  *
263  * Returns: the address of a newly allocated memory block.
264  */
265 gpointer
266 mono_mempool_alloc (MonoMemPool *pool, guint size)
267 {
268         gpointer rval;
269
270         size = ALIGN_SIZE (size);
271
272 #ifdef MALLOC_ALLOCATION
273         {
274                 Chunk *c = g_malloc (size + sizeof (Chunk));
275
276                 c->next = pool->chunks;
277                 pool->chunks = c;
278                 c->size = size - sizeof(Chunk);
279
280                 pool->allocated += size;
281
282                 rval = ((guint8*)c) + sizeof (Chunk);
283         }
284 #else
285         rval = pool->pos;
286         pool->pos = (guint8*)rval + size;
287
288 #ifdef TRACE_ALLOCATIONS
289         if (pool == mono_get_corlib ()->mempool) {
290                 mono_backtrace (size);
291         }
292 #endif
293         if (G_UNLIKELY (pool->pos >= pool->end)) {
294                 pool->pos -= size;
295                 if (size >= 4096) {
296                         MonoMemPool *np = g_malloc (SIZEOF_MEM_POOL + size);
297                         np->next = pool->next;
298                         pool->next = np;
299                         np->pos = (guint8*)np + SIZEOF_MEM_POOL;
300                         np->size = SIZEOF_MEM_POOL + size;
301                         np->end = np->pos + np->size - SIZEOF_MEM_POOL;
302                         pool->d.allocated += SIZEOF_MEM_POOL + size;
303                         total_bytes_allocated += SIZEOF_MEM_POOL + size;
304                         return (guint8*)np + SIZEOF_MEM_POOL;
305                 } else {
306                         int new_size = get_next_size (pool, size);
307                         MonoMemPool *np = g_malloc (new_size);
308                         np->next = pool->next;
309                         pool->next = np;
310                         pool->pos = (guint8*)np + SIZEOF_MEM_POOL;
311                         np->pos = (guint8*)np + SIZEOF_MEM_POOL;
312                         np->size = new_size;
313                         np->end = np->pos;
314                         pool->end = pool->pos + new_size - SIZEOF_MEM_POOL;
315                         pool->d.allocated += new_size;
316                         total_bytes_allocated += new_size;
317
318                         rval = pool->pos;
319                         pool->pos += size;
320                 }
321         }
322 #endif
323
324         return rval;
325 }
326
327 /**
328  * mono_mempool_alloc0:
329  *
330  * same as mono_mempool_alloc, but fills memory with zero.
331  */
332 gpointer
333 mono_mempool_alloc0 (MonoMemPool *pool, guint size)
334 {
335         gpointer rval;
336
337 #ifdef MALLOC_ALLOCATION
338         rval = mono_mempool_alloc (pool, size);
339 #else
340         size = ALIGN_SIZE (size);
341
342         rval = pool->pos;
343         pool->pos = (guint8*)rval + size;
344
345         if (G_UNLIKELY (pool->pos >= pool->end)) {
346                 rval = mono_mempool_alloc (pool, size);
347         }
348 #ifdef TRACE_ALLOCATIONS
349         else if (pool == mono_get_corlib ()->mempool) {
350                 mono_backtrace (size);
351         }
352 #endif
353 #endif
354
355         memset (rval, 0, size);
356         return rval;
357 }
358
359 /**
360  * mono_mempool_contains_addr:
361  *
362  *  Determines whenever ADDR is inside the memory used by the mempool.
363  */
364 gboolean
365 mono_mempool_contains_addr (MonoMemPool *pool,
366                                                         gpointer addr)
367 {
368 #ifdef MALLOC_ALLOCATION
369         Chunk *c;
370
371         c = pool->chunks;
372         while (c) {
373                 guint8 *p = ((guint8*)c) + sizeof (Chunk);
374
375                 if (addr >= (gpointer)p && addr < (gpointer)(p + c->size))
376                         return TRUE;
377
378                 c = c->next;
379         }
380 #else
381         MonoMemPool *p;
382
383         p = pool;
384         while (p) {
385                 if (addr > (gpointer)p && addr <= (gpointer)((guint8*)p + p->size))
386                         return TRUE;
387                 p = p->next;
388         }
389 #endif
390
391         return FALSE;
392 }
393
394 /**
395  * mono_mempool_strdup:
396  *
397  * Same as strdup, but allocates memory from the mempool.
398  * Returns: a pointer to the newly allocated string data inside the mempool.
399  */
400 char*
401 mono_mempool_strdup (MonoMemPool *pool,
402                                          const char *s)
403 {
404         int l;
405         char *res;
406
407         if (s == NULL)
408                 return NULL;
409
410         l = strlen (s);
411         res = mono_mempool_alloc (pool, l + 1);
412         memcpy (res, s, l + 1);
413
414         return res;
415 }
416
417 /**
418  * mono_mempool_get_allocated:
419  *
420  * Return the amount of memory allocated for this mempool.
421  */
422 guint32
423 mono_mempool_get_allocated (MonoMemPool *pool)
424 {
425 #ifdef MALLOC_ALLOCATION
426         return pool->allocated;
427 #else
428         return pool->d.allocated;
429 #endif
430 }
431
432 /**
433  * mono_mempool_get_bytes_allocated:
434  *
435  * Return the number of bytes currently allocated for mempools.
436  */
437 long
438 mono_mempool_get_bytes_allocated (void)
439 {
440         return total_bytes_allocated;
441 }