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