First set of licensing changes
[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  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14  */
15
16 #include <config.h>
17 #include <glib.h>
18 #include <string.h>
19
20 #include "mempool.h"
21 #include "mempool-internals.h"
22
23 /*
24  * MonoMemPool is for fast allocation of memory. We free
25  * all memory when the pool is destroyed.
26  */
27
28 #define MEM_ALIGN 8
29 #define ALIGN_SIZE(s)   (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1))
30
31 // Size of memory at start of mempool reserved for header
32 #define SIZEOF_MEM_POOL (ALIGN_SIZE (sizeof (MonoMemPool)))
33
34 #if MONO_SMALL_CONFIG
35 #define MONO_MEMPOOL_PAGESIZE 4096
36 #define MONO_MEMPOOL_MINSIZE 256
37 #else
38 #define MONO_MEMPOOL_PAGESIZE 8192
39 #define MONO_MEMPOOL_MINSIZE 512
40 #endif
41
42 // The --with-malloc-mempools debug-build flag causes mempools to be allocated in single-element blocks, so tools like Valgrind can run better.
43 #if USE_MALLOC_FOR_MEMPOOLS
44 #define INDIVIDUAL_ALLOCATIONS
45 #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE 0
46 #else
47 #define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE MONO_MEMPOOL_PAGESIZE
48 #endif
49
50 #ifndef G_LIKELY
51 #define G_LIKELY(a) (a)
52 #define G_UNLIKELY(a) (a)
53 #endif
54
55 // A mempool is a linked list of memory blocks, each of which begins with this header structure.
56 // The initial block in the linked list is special, and tracks additional information.
57 struct _MonoMemPool {
58         // Next block after this one in linked list
59         MonoMemPool *next;
60
61         // Size of this memory block only
62         guint32 size;
63
64         // Used in "initial block" only: Beginning of current free space in mempool (may be in some block other than the first one)
65         guint8 *pos;
66
67         // Used in "initial block" only: End of current free space in mempool (ie, the first byte following the end of usable space)
68         guint8 *end;
69
70         union {
71                 // Unused: Imposing floating point memory rules on _MonoMemPool's final field ensures proper alignment of whole header struct
72                 double pad;
73
74                 // Used in "initial block" only: Number of bytes so far allocated (whether used or not) in the whole mempool
75                 guint32 allocated;
76         } d;
77 };
78
79 static long total_bytes_allocated = 0;
80
81 /**
82  * mono_mempool_new:
83  *
84  * Returns: a new memory pool.
85  */
86 MonoMemPool *
87 mono_mempool_new (void)
88 {
89         return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
90 }
91
92 /**
93  * mono_mempool_new_size:
94  * @initial_size: the amount of memory to initially reserve for the memory pool.
95  *
96  * Returns: a new memory pool with a specific initial memory reservation.
97  */
98 MonoMemPool *
99 mono_mempool_new_size (int initial_size)
100 {
101         MonoMemPool *pool;
102
103 #ifdef INDIVIDUAL_ALLOCATIONS
104         // In individual allocation mode, create initial block with zero storage space.
105         initial_size = SIZEOF_MEM_POOL;
106 #else
107         if (initial_size < MONO_MEMPOOL_MINSIZE)
108                 initial_size = MONO_MEMPOOL_MINSIZE;
109 #endif
110
111         pool = (MonoMemPool *)g_malloc (initial_size);
112
113         pool->next = NULL;
114         pool->pos = (guint8*)pool + SIZEOF_MEM_POOL; // Start after header
115         pool->end = (guint8*)pool + initial_size;    // End at end of allocated space 
116         pool->d.allocated = pool->size = initial_size;
117         total_bytes_allocated += initial_size;
118         return pool;
119 }
120
121 /**
122  * mono_mempool_destroy:
123  * @pool: the memory pool to destroy
124  *
125  * Free all memory associated with this pool.
126  */
127 void
128 mono_mempool_destroy (MonoMemPool *pool)
129 {
130         MonoMemPool *p, *n;
131
132         total_bytes_allocated -= pool->d.allocated;
133
134         p = pool;
135         while (p) {
136                 n = p->next;
137                 g_free (p);
138                 p = n;
139         }
140 }
141
142 /**
143  * mono_mempool_invalidate:
144  * @pool: the memory pool to invalidate
145  *
146  * Fill the memory associated with this pool to 0x2a (42). Useful for debugging.
147  */
148 void
149 mono_mempool_invalidate (MonoMemPool *pool)
150 {
151         MonoMemPool *p, *n;
152
153         p = pool;
154         while (p) {
155                 n = p->next;
156                 memset (p, 42, p->size);
157                 p = n;
158         }
159 }
160
161 /**
162  * mono_mempool_stats:
163  * @pool: the momory pool we need stats for
164  *
165  * Print a few stats about the mempool:
166  * - Total memory allocated (malloced) by mem pool
167  * - Number of chunks/blocks memory is allocated in
168  * - How much memory is available to dispense before a new malloc must occur?
169  */
170 void
171 mono_mempool_stats (MonoMemPool *pool)
172 {
173         MonoMemPool *p;
174         int count = 0;
175         guint32 still_free = pool->end - pool->pos;
176
177         p = pool;
178         while (p) {
179                 p = p->next;
180                 count++;
181         }
182         if (pool) {
183                 g_print ("Mempool %p stats:\n", pool);
184                 g_print ("Total mem allocated: %d\n", pool->d.allocated);
185                 g_print ("Num chunks: %d\n", count);
186                 g_print ("Free memory: %d\n", still_free);
187         }
188 }
189
190 #ifdef TRACE_ALLOCATIONS
191 #include <execinfo.h>
192 #include "metadata/appdomain.h"
193 #include "metadata/metadata-internals.h"
194
195 static mono_mutex_t mempool_tracing_lock;
196 #define BACKTRACE_DEPTH 7
197 static void
198 mono_backtrace (int size)
199 {
200         void *array[BACKTRACE_DEPTH];
201         char **names;
202         int i, symbols;
203         static gboolean inited;
204
205         if (!inited) {
206             mono_os_mutex_init_recursive (&mempool_tracing_lock);
207             inited = TRUE;
208         }
209
210         mono_os_mutex_lock (&mempool_tracing_lock);
211         g_print ("Allocating %d bytes\n", size);
212         symbols = backtrace (array, BACKTRACE_DEPTH);
213         names = backtrace_symbols (array, symbols);
214         for (i = 1; i < symbols; ++i) {
215                 g_print ("\t%s\n", names [i]);
216         }
217         free (names);
218         mono_os_mutex_unlock (&mempool_tracing_lock);
219 }
220
221 #endif
222
223 /**
224  * mono_mempool_alloc:
225  * @pool: the memory pool to use
226  * @size: size of the memory entity we are trying to allocate
227  *
228  * A mempool is growing; give a recommended size for the next block.
229  * Each block in a mempool should be about 150% bigger than the previous one,
230  * or bigger if it is necessary to include the new entity.
231  *
232  * Returns: the recommended size.
233  */
234 static guint
235 get_next_size (MonoMemPool *pool, int size)
236 {
237         int target = pool->next? pool->next->size: pool->size;
238         size += SIZEOF_MEM_POOL;
239         /* increase the size */
240         target += target / 2;
241         while (target < size) {
242                 target += target / 2;
243         }
244         if (target > MONO_MEMPOOL_PAGESIZE && size <= MONO_MEMPOOL_PAGESIZE)
245                 target = MONO_MEMPOOL_PAGESIZE;
246         return target;
247 }
248
249 /**
250  * mono_mempool_alloc:
251  * @pool: the memory pool to use
252  * @size: size of the memory block
253  *
254  * Allocates a new block of memory in @pool.
255  *
256  * Returns: the address of a newly allocated memory block.
257  */
258 gpointer
259 mono_mempool_alloc (MonoMemPool *pool, guint size)
260 {
261         gpointer rval = pool->pos; // Return value
262
263         // Normal case: Just bump up pos pointer and we are done
264         size = ALIGN_SIZE (size);
265         pool->pos = (guint8*)rval + size;
266
267 #ifdef TRACE_ALLOCATIONS
268         if (pool == mono_get_corlib ()->mempool) {
269                 mono_backtrace (size);
270         }
271 #endif
272
273         // If we have just overflowed the current block, we need to back up and try again.
274         if (G_UNLIKELY (pool->pos >= pool->end)) {
275                 pool->pos -= size;  // Back out
276
277                 // For large objects, allocate the object into its own block.
278                 // (In individual allocation mode, the constant will be 0 and this path will always be taken)
279                 if (size >= MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE) {
280                         guint new_size = SIZEOF_MEM_POOL + size;
281                         MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
282
283                         np->next = pool->next;
284                         np->size = new_size;
285                         pool->next = np;
286                         pool->d.allocated += new_size;
287                         total_bytes_allocated += new_size;
288
289                         rval = (guint8*)np + SIZEOF_MEM_POOL;
290                 } else {
291                         // Notice: any unused memory at the end of the old head becomes simply abandoned in this case until the mempool is freed (see Bugzilla #35136)
292                         guint new_size = get_next_size (pool, size);
293                         MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
294
295                         np->next = pool->next;
296                         np->size = new_size;
297                         pool->next = np;
298                         pool->pos = (guint8*)np + SIZEOF_MEM_POOL;
299                         pool->end = (guint8*)np + new_size;
300                         pool->d.allocated += new_size;
301                         total_bytes_allocated += new_size;
302
303                         rval = pool->pos;
304                         pool->pos += size;
305                 }
306         }
307
308         return rval;
309 }
310
311 /**
312  * mono_mempool_alloc0:
313  *
314  * same as mono_mempool_alloc, but fills memory with zero.
315  */
316 gpointer
317 mono_mempool_alloc0 (MonoMemPool *pool, guint size)
318 {
319         gpointer rval;
320
321         // For the fast path, repeat the first few lines of mono_mempool_alloc
322         size = ALIGN_SIZE (size);
323         rval = pool->pos;
324         pool->pos = (guint8*)rval + size;
325
326         // If that doesn't work fall back on mono_mempool_alloc to handle new chunk allocation
327         if (G_UNLIKELY (pool->pos >= pool->end)) {
328                 rval = mono_mempool_alloc (pool, size);
329         }
330 #ifdef TRACE_ALLOCATIONS
331         else if (pool == mono_get_corlib ()->mempool) {
332                 mono_backtrace (size);
333         }
334 #endif
335
336         memset (rval, 0, size);
337         return rval;
338 }
339
340 /**
341  * mono_mempool_contains_addr:
342  *
343  *  Determines whenever ADDR is inside the memory used by the mempool.
344  */
345 gboolean
346 mono_mempool_contains_addr (MonoMemPool *pool,
347                                                         gpointer addr)
348 {
349         MonoMemPool *p = pool;
350
351         while (p) {
352                 if (addr >= (gpointer)p && addr < (gpointer)((guint8*)p + p->size))
353                         return TRUE;
354                 p = p->next;
355         }
356
357         return FALSE;
358 }
359
360 /**
361  * mono_mempool_strdup:
362  *
363  * Same as strdup, but allocates memory from the mempool.
364  * Returns: a pointer to the newly allocated string data inside the mempool.
365  */
366 char*
367 mono_mempool_strdup (MonoMemPool *pool,
368                                          const char *s)
369 {
370         int l;
371         char *res;
372
373         if (s == NULL)
374                 return NULL;
375
376         l = strlen (s);
377         res = (char *)mono_mempool_alloc (pool, l + 1);
378         memcpy (res, s, l + 1);
379
380         return res;
381 }
382
383 /**
384  * mono_mempool_get_allocated:
385  *
386  * Return the amount of memory allocated for this mempool.
387  */
388 guint32
389 mono_mempool_get_allocated (MonoMemPool *pool)
390 {
391         return pool->d.allocated;
392 }
393
394 /**
395  * mono_mempool_get_bytes_allocated:
396  *
397  * Return the number of bytes currently allocated for mempools.
398  */
399 long
400 mono_mempool_get_bytes_allocated (void)
401 {
402         return total_bytes_allocated;
403 }