2009-05-22 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-hash.c
index 70cce73b0d35a26348012a35c13cccea6f2a0668..55d8adf05834f3ccab1a0fd323331e1bb27b9f53 100644 (file)
@@ -37,7 +37,6 @@
 #endif
 
 #include <glib.h>
-#include <mono/os/gc_wrapper.h>
 #include "mono-hash.h"
 #include "metadata/gc-internal.h"
 
@@ -93,6 +92,9 @@ static guint g_hash_table_foreach_remove_or_steal (MonoGHashTable     *hash_tabl
                                                    gpointer       user_data,
                                                    gboolean        notify);
 
+#ifdef HAVE_SGEN_GC
+static void mono_g_hash_mark (void *addr, MonoGCCopyFunc mark_func);
+#endif
 
 G_LOCK_DEFINE_STATIC (g_hash_global);
 
@@ -100,10 +102,32 @@ G_LOCK_DEFINE_STATIC (g_hash_global);
 static GMemChunk *node_mem_chunk = NULL;
 #endif
 #if defined(HAVE_SGEN_GC)
+static MonoGHashNode *node_free_lists [4] = {NULL};
+static void *hash_descr = NULL;
+static GMemChunk *node_mem_chunk = NULL;
+#else
 static void *node_gc_descs [4] = {NULL};
 static MonoGHashNode *node_free_lists [4] = {NULL};
+#endif
+
+#ifdef HAVE_SGEN_GC
+#define SET_NODE_KEY(node, gc_type, val) do { \
+       gpointer __val = (val); \
+       if (gc_type == MONO_HASH_KEY_GC || gc_type == MONO_HASH_KEY_VALUE_GC) \
+               MONO_ROOT_SETREF ((node), key, __val); \
+       else \
+         (node)->key = __val; \
+       } while (0)
+#define SET_NODE_VALUE(node, gc_type, val) do { \
+       gpointer __val = (val); \
+       if (gc_type == MONO_HASH_VALUE_GC || gc_type == MONO_HASH_KEY_VALUE_GC) \
+               MONO_ROOT_SETREF ((node), value, __val); \
+       else \
+         (node)->value = __val; \
+       } while (0)
 #else
-static MonoGHashNode *node_free_list = NULL;
+#define SET_NODE_KEY(node, gc_type, val) do { (node)->key = (val); } while (0)
+#define SET_NODE_VALUE(node, gc_type, val) do { (node)->value = (val); } while (0)
 #endif
 
 /**
@@ -139,6 +163,17 @@ mono_g_hash_table_new_type (GHashFunc    hash_func,
   MonoGHashTable *table = mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
   table->gc_type = type;
 #if defined(HAVE_SGEN_GC)
+  if (type < 0 || type > MONO_HASH_KEY_VALUE_GC)
+         g_error ("wrong type for gc hashtable");
+  /* 
+   * We use a user defined marking function to avoid having to register a GC root for
+   * each hash node.
+   */
+  if (!hash_descr)
+         hash_descr = mono_gc_make_root_descr_user (mono_g_hash_mark);
+  if (type != MONO_HASH_CONSERVATIVE_GC)
+         mono_gc_register_root_wbarrier ((char*)table, sizeof (MonoGHashTable), hash_descr);
+#elif defined(HAVE_BOEHM_GC)
   if (type < 0 || type > MONO_HASH_KEY_VALUE_GC)
          g_error ("wrong type for gc hashtable");
   if (!node_gc_descs [type] && type > MONO_HASH_CONSERVATIVE_GC) {
@@ -147,7 +182,10 @@ mono_g_hash_table_new_type (GHashFunc    hash_func,
                  bmap |= 1; /* the first field in the node is the key */
          if (type & MONO_HASH_VALUE_GC)
                  bmap |= 2; /* the second is the value */
-         node_gc_descs [type] = mono_gc_make_descr_from_bitmap (&bmap, 2);
+         bmap |= 4; /* next */
+         node_gc_descs [type] = mono_gc_make_descr_from_bitmap (&bmap, 3);
+
+         MONO_GC_REGISTER_ROOT (node_free_lists [type]);
   }
 #endif
   return table;
@@ -182,8 +220,8 @@ mono_g_hash_table_new_full (GHashFunc       hash_func,
 #if HAVE_BOEHM_GC
   static gboolean inited = FALSE;
   if (!inited) {
-    MONO_GC_REGISTER_ROOT (node_free_list);
-    inited = TRUE;
+         MONO_GC_REGISTER_ROOT (node_free_lists [0]);
+         inited = TRUE;
   }
   
   hash_table = GC_MALLOC (sizeof (MonoGHashTable));
@@ -230,6 +268,9 @@ mono_g_hash_table_destroy (MonoGHashTable *hash_table)
 
 #if HAVE_BOEHM_GC
 #else
+#if HAVE_SGEN_GC
+  mono_gc_deregister_root ((char*)hash_table);
+#endif
   g_free (hash_table->nodes);
   g_free (hash_table);
 #endif
@@ -327,17 +368,23 @@ g_hash_node_new (gpointer key,
   MonoGHashNode *hash_node = NULL;
 
 #if HAVE_BOEHM_GC
-  if (node_free_list) {
+  if (node_free_lists [gc_type]) {
          G_LOCK (g_hash_global);
 
-         if (node_free_list) {
-                 hash_node = node_free_list;
-                 node_free_list = node_free_list->next;
+         if (node_free_lists [gc_type]) {
+                 hash_node = node_free_lists [gc_type];
+                 node_free_lists [gc_type] = node_free_lists [gc_type]->next;
          }
          G_UNLOCK (g_hash_global);
   }
-  if (!hash_node)
-      hash_node = GC_MALLOC (sizeof (MonoGHashNode));
+  if (!hash_node) {
+         if (gc_type != MONO_HASH_CONSERVATIVE_GC) {
+                 //hash_node = GC_MALLOC (sizeof (MonoGHashNode));
+                 hash_node = GC_MALLOC_EXPLICITLY_TYPED (sizeof (MonoGHashNode), (GC_descr)node_gc_descs [gc_type]);
+         } else {
+                 hash_node = GC_MALLOC (sizeof (MonoGHashNode));
+         }
+  }
 #elif defined(HAVE_SGEN_GC)
   if (node_free_lists [gc_type]) {
          G_LOCK (g_hash_global);
@@ -348,8 +395,21 @@ g_hash_node_new (gpointer key,
          }
          G_UNLOCK (g_hash_global);
   }
-  if (!hash_node)
-      hash_node = mono_gc_alloc_fixed (sizeof (MonoGHashNode), node_gc_descs [gc_type]);
+  if (!hash_node) {
+         if (gc_type != MONO_HASH_CONSERVATIVE_GC) {
+                 /* 
+                  * Marking is handled by the marker function, no need to allocate GC visible
+                  * memory.
+                  */
+                 if (!node_mem_chunk)
+                         node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
+                                                                                               sizeof (MonoGHashNode),
+                                                                                               1024, G_ALLOC_ONLY);
+                 hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
+         } else {
+                 hash_node = mono_gc_alloc_fixed (sizeof (MonoGHashNode), NULL);
+         }
+  }
 #else
   G_LOCK (g_hash_global);
   if (node_free_list)
@@ -368,9 +428,9 @@ g_hash_node_new (gpointer key,
     }
   G_UNLOCK (g_hash_global);
 #endif
-  
-  hash_node->key = key;
-  hash_node->value = value;
+
+  SET_NODE_KEY (hash_node, gc_type, key);
+  SET_NODE_VALUE (hash_node, gc_type, value);
   hash_node->next = NULL;
   
   return hash_node;
@@ -415,7 +475,7 @@ mono_g_hash_table_insert (MonoGHashTable *hash_table,
       if (hash_table->value_destroy_func)
        hash_table->value_destroy_func ((*node)->value);
 
-      (*node)->value = value;
+         SET_NODE_VALUE ((*node), hash_table->gc_type, value);
     }
   else
     {
@@ -457,8 +517,8 @@ mono_g_hash_table_replace (MonoGHashTable *hash_table,
       if (hash_table->value_destroy_func)
        hash_table->value_destroy_func ((*node)->value);
 
-      (*node)->key   = key;
-      (*node)->value = value;
+         SET_NODE_KEY ((*node), hash_table->gc_type, key);
+         SET_NODE_VALUE ((*node), hash_table->gc_type, value);
     }
   else
     {
@@ -704,8 +764,10 @@ mono_g_hash_table_remap (MonoGHashTable *hash_table,
   g_return_if_fail (func != NULL);
   
   for (i = 0; i < hash_table->size; i++)
-    for (node = hash_table->nodes[i]; node; node = node->next)
-      node->value = (* func) (node->key, node->value, user_data);
+         for (node = hash_table->nodes[i]; node; node = node->next) {
+                 gpointer new_val = (* func) (node->key, node->value, user_data);
+                 SET_NODE_VALUE (node, hash_table->gc_type, new_val);
+         }
 }
 
 static void
@@ -761,7 +823,7 @@ g_hash_node_destroy (MonoGHashNode      *hash_node,
   hash_node->value = NULL;
 
   G_LOCK (g_hash_global);
-#if defined(HAVE_SGEN_GC)
+#if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
   hash_node->next = node_free_lists [type];
   node_free_lists [type] = hash_node;
 #else
@@ -803,7 +865,7 @@ g_hash_nodes_destroy (MonoGHashNode *hash_node,
       node->value = NULL;
  
       G_LOCK (g_hash_global);
-#if defined(HAVE_SGEN_GC)
+#if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
       node->next = node_free_lists [type];
       node_free_lists [type] = hash_node;
 #else
@@ -813,3 +875,41 @@ g_hash_nodes_destroy (MonoGHashNode *hash_node,
       G_UNLOCK (g_hash_global);
     }
 }
+
+#ifdef HAVE_SGEN_GC
+
+/* GC marker function */
+static void
+mono_g_hash_mark (void *addr, MonoGCCopyFunc mark_func)
+{
+       MonoGHashTable *table = (MonoGHashTable*)addr;
+       MonoGHashNode *node;
+       int i;
+
+       if (table->gc_type == MONO_HASH_KEY_GC) {
+               for (i = 0; i < table->size; i++) {
+                       for (node = table->nodes [i]; node; node = node->next) {
+                               if (node->key)
+                                       SET_NODE_KEY (node, table->gc_type, mark_func (node->key));
+                       }
+               }
+       } else if (table->gc_type == MONO_HASH_VALUE_GC) {
+               for (i = 0; i < table->size; i++) {
+                       for (node = table->nodes [i]; node; node = node->next) {
+                               if (node->value)
+                                       SET_NODE_VALUE (node, table->gc_type, mark_func (node->value));
+                       }
+               }
+       } else if (table->gc_type == MONO_HASH_KEY_VALUE_GC) {
+               for (i = 0; i < table->size; i++) {
+                       for (node = table->nodes [i]; node; node = node->next) {
+                               if (node->key)
+                                       SET_NODE_KEY (node, table->gc_type, mark_func (node->key));
+                               if (node->value)
+                                       SET_NODE_VALUE (node, table->gc_type, mark_func (node->value));
+                       }
+               }
+       }
+}
+
+#endif