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