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