Mon Oct 6 16:10:02 CEST 2008 Paolo Molaro <lupus@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 void
199 mono_backtrace (int limit)
200 {
201         void *array[limit];
202         char **names;
203         int i;
204         backtrace (array, limit);
205         names = backtrace_symbols (array, limit);
206         for (i = 1; i < limit; ++i) {
207                 g_print ("\t%s\n", names [i]);
208         }
209         g_free (names);
210 }
211
212 #endif
213
214 static int
215 get_next_size (MonoMemPool *pool, int size)
216 {
217         int target = pool->next? pool->next->size: pool->size;
218         size += sizeof (MonoMemPool);
219         /* increase the size */
220         target += target / 2;
221         while (target < size) {
222                 target += target / 2;
223         }
224         if (target > MONO_MEMPOOL_PAGESIZE)
225                 target = MONO_MEMPOOL_PAGESIZE;
226         /* we are called with size smaller than 4096 */
227         g_assert (size <= MONO_MEMPOOL_PAGESIZE);
228         return target;
229 }
230 #endif
231
232 /**
233  * mono_mempool_alloc:
234  * @pool: the momory pool to use
235  * @size: size of the momory block
236  *
237  * Allocates a new block of memory in @pool.
238  *
239  * Returns: the address of a newly allocated memory block.
240  */
241 gpointer
242 mono_mempool_alloc (MonoMemPool *pool, guint size)
243 {
244         gpointer rval;
245         
246         size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);
247
248 #ifdef MALLOC_ALLOCATION
249         {
250                 Chunk *c = g_malloc (sizeof (Chunk) + size);
251
252                 c->next = pool->chunks;
253                 pool->chunks = c;
254                 c->size = size;
255
256                 pool->allocated += size;
257
258                 rval = ((guint8*)c) + sizeof (Chunk);
259         }
260 #else
261         rval = pool->pos;
262         pool->pos = (guint8*)rval + size;
263
264 #ifdef TRACE_ALLOCATIONS
265         if (pool == mono_get_corlib ()->mempool) {
266                 g_print ("Allocating %d bytes\n", size);
267                 mono_backtrace (7);
268         }
269 #endif
270         if (G_UNLIKELY (pool->pos >= pool->end)) {
271                 pool->pos -= size;
272                 if (size >= 4096) {
273                         MonoMemPool *np = g_malloc (sizeof (MonoMemPool) + size);
274                         np->next = pool->next;
275                         pool->next = np;
276                         np->pos = (guint8*)np + sizeof (MonoMemPool);
277                         np->size = sizeof (MonoMemPool) + size;
278                         np->end = np->pos + np->size - sizeof (MonoMemPool);
279                         pool->d.allocated += sizeof (MonoMemPool) + size;
280                         return (guint8*)np + sizeof (MonoMemPool);
281                 } else {
282                         int new_size = get_next_size (pool, size);
283                         MonoMemPool *np = g_malloc (new_size);
284                         np->next = pool->next;
285                         pool->next = np;
286                         pool->pos = (guint8*)np + sizeof (MonoMemPool);
287                         np->pos = (guint8*)np + sizeof (MonoMemPool);
288                         np->size = new_size;
289                         np->end = np->pos;
290                         pool->end = pool->pos + new_size - sizeof (MonoMemPool);
291                         pool->d.allocated += new_size;
292
293                         rval = pool->pos;
294                         pool->pos += size;
295                 }
296         }
297
298         return rval;
299 #endif
300 }
301
302 /**
303  * mono_mempool_alloc0:
304  *
305  * same as mono_mempool_alloc, but fills memory with zero.
306  */
307 gpointer
308 mono_mempool_alloc0 (MonoMemPool *pool, guint size)
309 {
310         gpointer rval;
311
312 #ifdef MALLOC_ALLOCATION
313         rval = mono_mempool_alloc (pool, size);
314 #else
315         size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);
316
317         rval = pool->pos;
318         pool->pos = (guint8*)rval + size;
319
320         if (G_UNLIKELY (pool->pos >= pool->end)) {
321                 rval = mono_mempool_alloc (pool, size);
322         }
323 #endif
324
325         memset (rval, 0, size);
326         return rval;
327 }
328
329 /**
330  * mono_mempool_contains_addr:
331  *
332  *  Determines whenever ADDR is inside the memory used by the mempool.
333  */
334 gboolean
335 mono_mempool_contains_addr (MonoMemPool *pool,
336                                                         gpointer addr)
337 {
338 #ifdef MALLOC_ALLOCATION
339         Chunk *c;
340
341         c = pool->chunks;
342         while (c) {
343                 guint8 *p = ((guint8*)c) + sizeof (Chunk);
344
345                 if (addr >= (gpointer)p && addr < (gpointer)(p + c->size))
346                         return TRUE;
347
348                 c = c->next;
349         }
350 #else
351         MonoMemPool *p;
352
353         p = pool;
354         while (p) {
355                 if (addr > (gpointer)p && addr <= (gpointer)((guint8*)p + p->size))
356                         return TRUE;
357                 p = p->next;
358         }
359 #endif
360
361         return FALSE;
362 }
363
364 /**
365  * mono_mempool_strdup:
366  *
367  * Same as strdup, but allocates memory from the mempool.
368  * Returns: a pointer to the newly allocated string data inside the mempool.
369  */
370 char*
371 mono_mempool_strdup (MonoMemPool *pool,
372                                          const char *s)
373 {
374         int l;
375         char *res;
376
377         if (s == NULL)
378                 return NULL;
379
380         l = strlen (s);
381         res = mono_mempool_alloc (pool, l + 1);
382         memcpy (res, s, l + 1);
383
384         return res;
385 }
386
387 /**
388  * mono_mempool_get_allocated:
389  *
390  * Return the amount of memory allocated for this mempool.
391  */
392 guint32
393 mono_mempool_get_allocated (MonoMemPool *pool)
394 {
395 #ifdef MALLOC_ALLOCATION
396         return pool->allocated;
397 #else
398         return pool->d.allocated;
399 #endif
400 }
401
402 GList*
403 g_list_prepend_mempool (MonoMemPool *mp, GList *list, gpointer data)
404 {
405         GList *new_list;
406         
407         new_list = mono_mempool_alloc (mp, sizeof (GList));
408         new_list->data = data;
409         new_list->prev = list ? list->prev : NULL;
410     new_list->next = list;
411
412     if (new_list->prev)
413             new_list->prev->next = new_list;
414     if (list)
415             list->prev = new_list;
416
417         return new_list;
418 }
419
420 GSList*
421 g_slist_prepend_mempool (MonoMemPool *mp, GSList *list, gpointer  data)
422 {
423         GSList *new_list;
424         
425         new_list = mono_mempool_alloc (mp, sizeof (GSList));
426         new_list->data = data;
427         new_list->next = list;
428
429         return new_list;
430 }