merge 99762:100015
[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
19 /*
20  * MonoMemPool is for fast allocation of memory. We free
21  * all memory when the pool is destroyed.
22  */
23
24 #define MEM_ALIGN 8
25
26 #define MONO_MEMPOOL_PAGESIZE 8192
27 #define MONO_MEMPOOL_MINSIZE 512
28
29 #ifndef G_LIKELY
30 #define G_LIKELY(a) (a)
31 #define G_UNLIKELY(a) (a)
32 #endif
33
34 struct _MonoMemPool {
35         MonoMemPool *next;
36         gint rest;
37         guint8 *pos, *end;
38         guint32 size;
39         union {
40                 double pad; /* to assure proper alignment */
41                 guint32 allocated;
42         } d;
43 };
44
45 /**
46  * mono_mempool_new:
47  *
48  * Returns: a new memory pool.
49  */
50 MonoMemPool *
51 mono_mempool_new (void)
52 {
53         return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
54 }
55
56 MonoMemPool *
57 mono_mempool_new_size (int initial_size)
58 {
59         MonoMemPool *pool;
60         if (initial_size < MONO_MEMPOOL_MINSIZE)
61                 initial_size = MONO_MEMPOOL_MINSIZE;
62         pool = g_malloc (initial_size);
63
64         pool->next = NULL;
65         pool->pos = (guint8*)pool + sizeof (MonoMemPool);
66         pool->end = pool->pos + initial_size - sizeof (MonoMemPool);
67         pool->d.allocated = pool->size = initial_size;
68         return pool;
69 }
70
71 /**
72  * mono_mempool_destroy:
73  * @pool: the memory pool to destroy
74  *
75  * Free all memory associated with this pool.
76  */
77 void
78 mono_mempool_destroy (MonoMemPool *pool)
79 {
80         MonoMemPool *p, *n;
81
82         p = pool;
83         while (p) {
84                 n = p->next;
85                 g_free (p);
86                 p = n;
87         }
88 }
89
90 /**
91  * mono_mempool_invalidate:
92  * @pool: the memory pool to invalidate
93  *
94  * Fill the memory associated with this pool to 0x2a (42). Useful for debugging.
95  */
96 void
97 mono_mempool_invalidate (MonoMemPool *pool)
98 {
99         MonoMemPool *p, *n;
100
101         p = pool;
102         while (p) {
103                 n = p->next;
104                 memset (p, 42, p->size);
105                 p = n;
106         }
107 }
108
109 void
110 mono_mempool_empty (MonoMemPool *pool)
111 {
112         pool->pos = (guint8*)pool + sizeof (MonoMemPool);
113         pool->end = pool->pos + pool->size - sizeof (MonoMemPool);
114 }
115
116 /**
117  * mono_mempool_stats:
118  * @pool: the momory pool we need stats for
119  *
120  * Print a few stats about the mempool
121  */
122 void
123 mono_mempool_stats (MonoMemPool *pool)
124 {
125         MonoMemPool *p;
126         int count = 0;
127         guint32 still_free = 0;
128
129         p = pool;
130         while (p) {
131                 still_free += p->end - p->pos;
132                 p = p->next;
133                 count++;
134         }
135         if (pool) {
136                 g_print ("Mempool %p stats:\n", pool);
137                 g_print ("Total mem allocated: %d\n", pool->d.allocated);
138                 g_print ("Num chunks: %d\n", count);
139                 g_print ("Free memory: %d\n", still_free);
140         }
141 }
142
143 #ifdef TRACE_ALLOCATIONS
144 #include <execinfo.h>
145 #include "metadata/appdomain.h"
146 #include "metadata/metadata-internals.h"
147
148 static void
149 mono_backtrace (int limit)
150 {
151         void *array[limit];
152         char **names;
153         int i;
154         backtrace (array, limit);
155         names = backtrace_symbols (array, limit);
156         for (i = 1; i < limit; ++i) {
157                 g_print ("\t%s\n", names [i]);
158         }
159         g_free (names);
160 }
161
162 #endif
163
164 static int
165 get_next_size (MonoMemPool *pool, int size)
166 {
167         int target = pool->next? pool->next->size: pool->size;
168         size += sizeof (MonoMemPool);
169         /* increase the size */
170         target += target / 2;
171         while (target < size) {
172                 target += target / 2;
173         }
174         if (target > MONO_MEMPOOL_PAGESIZE)
175                 target = MONO_MEMPOOL_PAGESIZE;
176         /* we are called with size smaller than 4096 */
177         g_assert (size <= MONO_MEMPOOL_PAGESIZE);
178         return target;
179 }
180
181 /**
182  * mono_mempool_alloc:
183  * @pool: the momory pool to destroy
184  * @size: size of the momory block
185  *
186  * Allocates a new block of memory in @pool.
187  *
188  * Returns: the address of a newly allocated memory block.
189  */
190 gpointer
191 mono_mempool_alloc (MonoMemPool *pool, guint size)
192 {
193         gpointer rval;
194         
195         size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);
196
197         rval = pool->pos;
198         pool->pos = (guint8*)rval + size;
199
200 #ifdef TRACE_ALLOCATIONS
201         if (pool == mono_get_corlib ()->mempool) {
202                 g_print ("Allocating %d bytes\n", size);
203                 mono_backtrace (7);
204         }
205 #endif
206         if (G_UNLIKELY (pool->pos >= pool->end)) {
207                 pool->pos -= size;
208                 if (size >= 4096) {
209                         MonoMemPool *np = g_malloc (sizeof (MonoMemPool) + size);
210                         np->next = pool->next;
211                         pool->next = np;
212                         np->pos = (guint8*)np + sizeof (MonoMemPool);
213                         np->size = sizeof (MonoMemPool) + size;
214                         np->end = np->pos + np->size - sizeof (MonoMemPool);
215                         pool->d.allocated += sizeof (MonoMemPool) + size;
216                         return (guint8*)np + sizeof (MonoMemPool);
217                 } else {
218                         int new_size = get_next_size (pool, size);
219                         MonoMemPool *np = g_malloc (new_size);
220                         np->next = pool->next;
221                         pool->next = np;
222                         pool->pos = (guint8*)np + sizeof (MonoMemPool);
223                         np->pos = (guint8*)np + sizeof (MonoMemPool);
224                         np->size = new_size;
225                         np->end = np->pos;
226                         pool->end = pool->pos + new_size - sizeof (MonoMemPool);
227                         pool->d.allocated += new_size;
228
229                         rval = pool->pos;
230                         pool->pos += size;
231                 }
232         }
233
234         return rval;
235 }
236
237 /**
238  * mono_mempool_alloc0:
239  *
240  * same as mono_mempool_alloc, but fills memory with zero.
241  */
242 gpointer
243 mono_mempool_alloc0 (MonoMemPool *pool, guint size)
244 {
245         gpointer rval;
246         
247         size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1);
248
249         rval = pool->pos;
250         pool->pos = (guint8*)rval + size;
251
252         if (G_UNLIKELY (pool->pos >= pool->end)) {
253                 rval = mono_mempool_alloc (pool, size);
254         }
255
256         memset (rval, 0, size);
257         return rval;
258 }
259
260 /**
261  * mono_mempool_contains_addr:
262  *
263  *  Determines whenever ADDR is inside the memory used by the mempool.
264  */
265 gboolean
266 mono_mempool_contains_addr (MonoMemPool *pool,
267                                                         gpointer addr)
268 {
269         MonoMemPool *p;
270
271         p = pool;
272         while (p) {
273                 if (addr > (gpointer)p && addr <= (gpointer)((guint8*)p + p->size))
274                         return TRUE;
275                 p = p->next;
276         }
277
278         return FALSE;
279 }
280
281 /**
282  * mono_mempool_strdup:
283  *
284  * Same as strdup, but allocates memory from the mempool.
285  * Returns: a pointer to the newly allocated string data inside the mempool.
286  */
287 char*
288 mono_mempool_strdup (MonoMemPool *pool,
289                                          const char *s)
290 {
291         int l;
292         char *res;
293
294         if (s == NULL)
295                 return NULL;
296
297         l = strlen (s);
298         res = mono_mempool_alloc (pool, l + 1);
299         memcpy (res, s, l + 1);
300
301         return res;
302 }
303
304 /**
305  * mono_mempool_get_allocated:
306  *
307  * Return the amount of memory allocated for this mempool.
308  */
309 guint32
310 mono_mempool_get_allocated (MonoMemPool *pool)
311 {
312         return pool->d.allocated;
313 }