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