First set of licensing changes
[mono.git] / mono / metadata / mempool.c
index f02703a31fa2b41e83600a8099d668f944931f13..60d36ae2ad1765d76cbf450dbb8feb9b616bf479 100644 (file)
@@ -10,6 +10,7 @@
  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  * Copyright 2011 Xamarin Inc. (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
 #include "mempool.h"
 #include "mempool-internals.h"
 
-#if USE_MALLOC_FOR_MEMPOOLS
-#define MALLOC_ALLOCATION
-#endif
-
 /*
  * MonoMemPool is for fast allocation of memory. We free
  * all memory when the pool is destroyed.
@@ -30,6 +27,8 @@
 
 #define MEM_ALIGN 8
 #define ALIGN_SIZE(s)  (((s) + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1))
+
+// Size of memory at start of mempool reserved for header
 #define SIZEOF_MEM_POOL        (ALIGN_SIZE (sizeof (MonoMemPool)))
 
 #if MONO_SMALL_CONFIG
 #define MONO_MEMPOOL_MINSIZE 512
 #endif
 
+// The --with-malloc-mempools debug-build flag causes mempools to be allocated in single-element blocks, so tools like Valgrind can run better.
+#if USE_MALLOC_FOR_MEMPOOLS
+#define INDIVIDUAL_ALLOCATIONS
+#define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE 0
+#else
+#define MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE MONO_MEMPOOL_PAGESIZE
+#endif
+
 #ifndef G_LIKELY
 #define G_LIKELY(a) (a)
 #define G_UNLIKELY(a) (a)
 #endif
 
-#ifdef MALLOC_ALLOCATION
-typedef struct _Chunk {
-       struct _Chunk *next;
-       guint32 size;
-} Chunk;
-
-struct _MonoMemPool {
-       Chunk *chunks;
-       guint32 allocated;
-};
-#else
+// A mempool is a linked list of memory blocks, each of which begins with this header structure.
+// The initial block in the linked list is special, and tracks additional information.
 struct _MonoMemPool {
+       // Next block after this one in linked list
        MonoMemPool *next;
-       gint rest;
-       guint8 *pos, *end;
+
+       // Size of this memory block only
        guint32 size;
+
+       // Used in "initial block" only: Beginning of current free space in mempool (may be in some block other than the first one)
+       guint8 *pos;
+
+       // Used in "initial block" only: End of current free space in mempool (ie, the first byte following the end of usable space)
+       guint8 *end;
+
        union {
-               double pad; /* to assure proper alignment */
+               // Unused: Imposing floating point memory rules on _MonoMemPool's final field ensures proper alignment of whole header struct
+               double pad;
+
+               // Used in "initial block" only: Number of bytes so far allocated (whether used or not) in the whole mempool
                guint32 allocated;
        } d;
 };
-#endif
 
 static long total_bytes_allocated = 0;
 
@@ -81,24 +89,33 @@ mono_mempool_new (void)
        return mono_mempool_new_size (MONO_MEMPOOL_PAGESIZE);
 }
 
+/**
+ * mono_mempool_new_size:
+ * @initial_size: the amount of memory to initially reserve for the memory pool.
+ *
+ * Returns: a new memory pool with a specific initial memory reservation.
+ */
 MonoMemPool *
 mono_mempool_new_size (int initial_size)
 {
-#ifdef MALLOC_ALLOCATION
-       return g_new0 (MonoMemPool, 1);
-#else
        MonoMemPool *pool;
+
+#ifdef INDIVIDUAL_ALLOCATIONS
+       // In individual allocation mode, create initial block with zero storage space.
+       initial_size = SIZEOF_MEM_POOL;
+#else
        if (initial_size < MONO_MEMPOOL_MINSIZE)
                initial_size = MONO_MEMPOOL_MINSIZE;
-       pool = g_malloc (initial_size);
+#endif
+
+       pool = (MonoMemPool *)g_malloc (initial_size);
 
        pool->next = NULL;
-       pool->pos = (guint8*)pool + SIZEOF_MEM_POOL;
-       pool->end = pool->pos + initial_size - SIZEOF_MEM_POOL;
+       pool->pos = (guint8*)pool + SIZEOF_MEM_POOL; // Start after header
+       pool->end = (guint8*)pool + initial_size;    // End at end of allocated space 
        pool->d.allocated = pool->size = initial_size;
        total_bytes_allocated += initial_size;
        return pool;
-#endif
 }
 
 /**
@@ -110,11 +127,6 @@ mono_mempool_new_size (int initial_size)
 void
 mono_mempool_destroy (MonoMemPool *pool)
 {
-#ifdef MALLOC_ALLOCATION
-       mono_mempool_empty (pool);
-
-       g_free (pool);
-#else
        MonoMemPool *p, *n;
 
        total_bytes_allocated -= pool->d.allocated;
@@ -125,7 +137,6 @@ mono_mempool_destroy (MonoMemPool *pool)
                g_free (p);
                p = n;
        }
-#endif
 }
 
 /**
@@ -137,9 +148,6 @@ mono_mempool_destroy (MonoMemPool *pool)
 void
 mono_mempool_invalidate (MonoMemPool *pool)
 {
-#ifdef MALLOC_ALLOCATION
-       g_assert_not_reached ();
-#else
        MonoMemPool *p, *n;
 
        p = pool;
@@ -148,49 +156,26 @@ mono_mempool_invalidate (MonoMemPool *pool)
                memset (p, 42, p->size);
                p = n;
        }
-#endif
-}
-
-void
-mono_mempool_empty (MonoMemPool *pool)
-{
-#ifdef MALLOC_ALLOCATION
-       Chunk *p, *n;
-
-       p = pool->chunks;
-       pool->chunks = NULL;
-       while (p) {
-               n = p->next;
-               g_free (p);
-               p = n;
-       }
-
-       pool->allocated = 0;
-#else
-       pool->pos = (guint8*)pool + SIZEOF_MEM_POOL;
-       pool->end = pool->pos + pool->size - SIZEOF_MEM_POOL;
-#endif
 }
 
 /**
  * mono_mempool_stats:
  * @pool: the momory pool we need stats for
  *
- * Print a few stats about the mempool
+ * Print a few stats about the mempool:
+ * - Total memory allocated (malloced) by mem pool
+ * - Number of chunks/blocks memory is allocated in
+ * - How much memory is available to dispense before a new malloc must occur?
  */
 void
 mono_mempool_stats (MonoMemPool *pool)
 {
-#ifdef MALLOC_ALLOCATION
-       g_assert_not_reached ();
-#else
        MonoMemPool *p;
        int count = 0;
-       guint32 still_free = 0;
+       guint32 still_free = pool->end - pool->pos;
 
        p = pool;
        while (p) {
-               still_free += p->end - p->pos;
                p = p->next;
                count++;
        }
@@ -200,10 +185,8 @@ mono_mempool_stats (MonoMemPool *pool)
                g_print ("Num chunks: %d\n", count);
                g_print ("Free memory: %d\n", still_free);
        }
-#endif
 }
 
-#ifndef MALLOC_ALLOCATION
 #ifdef TRACE_ALLOCATIONS
 #include <execinfo.h>
 #include "metadata/appdomain.h"
@@ -220,11 +203,11 @@ mono_backtrace (int size)
         static gboolean inited;
 
         if (!inited) {
-            mono_mutex_init_recursive (&mempool_tracing_lock);
+            mono_os_mutex_init_recursive (&mempool_tracing_lock);
             inited = TRUE;
         }
 
-        mono_mutex_lock (&mempool_tracing_lock);
+        mono_os_mutex_lock (&mempool_tracing_lock);
         g_print ("Allocating %d bytes\n", size);
         symbols = backtrace (array, BACKTRACE_DEPTH);
         names = backtrace_symbols (array, symbols);
@@ -232,12 +215,23 @@ mono_backtrace (int size)
                 g_print ("\t%s\n", names [i]);
         }
         free (names);
-        mono_mutex_unlock (&mempool_tracing_lock);
+        mono_os_mutex_unlock (&mempool_tracing_lock);
 }
 
 #endif
 
-static int
+/**
+ * mono_mempool_alloc:
+ * @pool: the memory pool to use
+ * @size: size of the memory entity we are trying to allocate
+ *
+ * A mempool is growing; give a recommended size for the next block.
+ * Each block in a mempool should be about 150% bigger than the previous one,
+ * or bigger if it is necessary to include the new entity.
+ *
+ * Returns: the recommended size.
+ */
+static guint
 get_next_size (MonoMemPool *pool, int size)
 {
        int target = pool->next? pool->next->size: pool->size;
@@ -251,12 +245,11 @@ get_next_size (MonoMemPool *pool, int size)
                target = MONO_MEMPOOL_PAGESIZE;
        return target;
 }
-#endif
 
 /**
  * mono_mempool_alloc:
- * @pool: the momory pool to use
- * @size: size of the momory block
+ * @pool: the memory pool to use
+ * @size: size of the memory block
  *
  * Allocates a new block of memory in @pool.
  *
@@ -265,24 +258,10 @@ get_next_size (MonoMemPool *pool, int size)
 gpointer
 mono_mempool_alloc (MonoMemPool *pool, guint size)
 {
-       gpointer rval;
+       gpointer rval = pool->pos; // Return value
 
+       // Normal case: Just bump up pos pointer and we are done
        size = ALIGN_SIZE (size);
-
-#ifdef MALLOC_ALLOCATION
-       {
-               Chunk *c = g_malloc (size + sizeof (Chunk));
-
-               c->next = pool->chunks;
-               pool->chunks = c;
-               c->size = size - sizeof(Chunk);
-
-               pool->allocated += size;
-
-               rval = ((guint8*)c) + sizeof (Chunk);
-       }
-#else
-       rval = pool->pos;
        pool->pos = (guint8*)rval + size;
 
 #ifdef TRACE_ALLOCATIONS
@@ -290,28 +269,34 @@ mono_mempool_alloc (MonoMemPool *pool, guint size)
                mono_backtrace (size);
        }
 #endif
+
+       // If we have just overflowed the current block, we need to back up and try again.
        if (G_UNLIKELY (pool->pos >= pool->end)) {
-               pool->pos -= size;
-               if (size >= 4096) {
-                       MonoMemPool *np = g_malloc (SIZEOF_MEM_POOL + size);
+               pool->pos -= size;  // Back out
+
+               // For large objects, allocate the object into its own block.
+               // (In individual allocation mode, the constant will be 0 and this path will always be taken)
+               if (size >= MONO_MEMPOOL_PREFER_INDIVIDUAL_ALLOCATION_SIZE) {
+                       guint new_size = SIZEOF_MEM_POOL + size;
+                       MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
+
                        np->next = pool->next;
+                       np->size = new_size;
                        pool->next = np;
-                       np->pos = (guint8*)np + SIZEOF_MEM_POOL;
-                       np->size = SIZEOF_MEM_POOL + size;
-                       np->end = np->pos + np->size - SIZEOF_MEM_POOL;
-                       pool->d.allocated += SIZEOF_MEM_POOL + size;
-                       total_bytes_allocated += SIZEOF_MEM_POOL + size;
-                       return (guint8*)np + SIZEOF_MEM_POOL;
+                       pool->d.allocated += new_size;
+                       total_bytes_allocated += new_size;
+
+                       rval = (guint8*)np + SIZEOF_MEM_POOL;
                } else {
-                       int new_size = get_next_size (pool, size);
-                       MonoMemPool *np = g_malloc (new_size);
+                       // Notice: any unused memory at the end of the old head becomes simply abandoned in this case until the mempool is freed (see Bugzilla #35136)
+                       guint new_size = get_next_size (pool, size);
+                       MonoMemPool *np = (MonoMemPool *)g_malloc (new_size);
+
                        np->next = pool->next;
+                       np->size = new_size;
                        pool->next = np;
                        pool->pos = (guint8*)np + SIZEOF_MEM_POOL;
-                       np->pos = (guint8*)np + SIZEOF_MEM_POOL;
-                       np->size = new_size;
-                       np->end = np->pos;
-                       pool->end = pool->pos + new_size - SIZEOF_MEM_POOL;
+                       pool->end = (guint8*)np + new_size;
                        pool->d.allocated += new_size;
                        total_bytes_allocated += new_size;
 
@@ -319,7 +304,6 @@ mono_mempool_alloc (MonoMemPool *pool, guint size)
                        pool->pos += size;
                }
        }
-#endif
 
        return rval;
 }
@@ -334,14 +318,12 @@ mono_mempool_alloc0 (MonoMemPool *pool, guint size)
 {
        gpointer rval;
 
-#ifdef MALLOC_ALLOCATION
-       rval = mono_mempool_alloc (pool, size);
-#else
+       // For the fast path, repeat the first few lines of mono_mempool_alloc
        size = ALIGN_SIZE (size);
-
        rval = pool->pos;
        pool->pos = (guint8*)rval + size;
 
+       // If that doesn't work fall back on mono_mempool_alloc to handle new chunk allocation
        if (G_UNLIKELY (pool->pos >= pool->end)) {
                rval = mono_mempool_alloc (pool, size);
        }
@@ -349,7 +331,6 @@ mono_mempool_alloc0 (MonoMemPool *pool, guint size)
        else if (pool == mono_get_corlib ()->mempool) {
                mono_backtrace (size);
        }
-#endif
 #endif
 
        memset (rval, 0, size);
@@ -365,28 +346,13 @@ gboolean
 mono_mempool_contains_addr (MonoMemPool *pool,
                                                        gpointer addr)
 {
-#ifdef MALLOC_ALLOCATION
-       Chunk *c;
-
-       c = pool->chunks;
-       while (c) {
-               guint8 *p = ((guint8*)c) + sizeof (Chunk);
+       MonoMemPool *p = pool;
 
-               if (addr >= (gpointer)p && addr < (gpointer)(p + c->size))
-                       return TRUE;
-
-               c = c->next;
-       }
-#else
-       MonoMemPool *p;
-
-       p = pool;
        while (p) {
-               if (addr > (gpointer)p && addr <= (gpointer)((guint8*)p + p->size))
+               if (addr >= (gpointer)p && addr < (gpointer)((guint8*)p + p->size))
                        return TRUE;
                p = p->next;
        }
-#endif
 
        return FALSE;
 }
@@ -408,7 +374,7 @@ mono_mempool_strdup (MonoMemPool *pool,
                return NULL;
 
        l = strlen (s);
-       res = mono_mempool_alloc (pool, l + 1);
+       res = (char *)mono_mempool_alloc (pool, l + 1);
        memcpy (res, s, l + 1);
 
        return res;
@@ -422,11 +388,7 @@ mono_mempool_strdup (MonoMemPool *pool,
 guint32
 mono_mempool_get_allocated (MonoMemPool *pool)
 {
-#ifdef MALLOC_ALLOCATION
-       return pool->allocated;
-#else
        return pool->d.allocated;
-#endif
 }
 
 /**