Uniform use of the return error from mono_dl
[mono.git] / mono / utils / mono-hash.c
index bd3479836e1468d0d6424af975256cae5ed6d0e5..c6c947b9f2a72e38fb46165e4493669d20bd847d 100644 (file)
 #endif
 
 #include <glib.h>
-#include <mono/os/gc_wrapper.h>
 #include "mono-hash.h"
+#include "metadata/gc-internal.h"
 
 #define HASH_TABLE_MIN_SIZE 11
 #define HASH_TABLE_MAX_SIZE 13845163
 
-
 typedef struct _MonoGHashNode      MonoGHashNode;
 
 struct _MonoGHashNode
@@ -62,6 +61,7 @@ struct _MonoGHashTable
   GEqualFunc       key_equal_func;
   GDestroyNotify   key_destroy_func;
   GDestroyNotify   value_destroy_func;
+  MonoGHashGCType  gc_type;
 };
 
 #define G_HASH_TABLE_RESIZE(hash_table)                                \
@@ -76,12 +76,13 @@ struct _MonoGHashTable
 static void            g_hash_table_resize       (MonoGHashTable         *hash_table);
 static MonoGHashNode** g_hash_table_lookup_node  (MonoGHashTable     *hash_table,
                                                    gconstpointer   key);
-static MonoGHashNode*  g_hash_node_new           (gpointer        key,
-                                                   gpointer        value);
+static MonoGHashNode*  g_hash_node_new           (gint            gc_type);
 static void            g_hash_node_destroy       (MonoGHashNode          *hash_node,
+                                                  MonoGHashGCType type,
                                                    GDestroyNotify  key_destroy_func,
                                                    GDestroyNotify  value_destroy_func);
 static void            g_hash_nodes_destroy      (MonoGHashNode          *hash_node,
+                                                  MonoGHashGCType type,
                                                  GDestroyNotify   key_destroy_func,
                                                  GDestroyNotify   value_destroy_func);
 static guint g_hash_table_foreach_remove_or_steal (MonoGHashTable     *hash_table,
@@ -89,13 +90,26 @@ 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);
 
-#if !HAVE_BOEHM_GC
+#if defined(HAVE_NULL_GC)
 static GMemChunk *node_mem_chunk = NULL;
-#endif
 static MonoGHashNode *node_free_list = NULL;
+#elif 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
+
+#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)
 
 /**
  * g_hash_table_new:
@@ -122,6 +136,45 @@ mono_g_hash_table_new (GHashFunc    hash_func,
   return mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
 }
 
+MonoGHashTable*
+mono_g_hash_table_new_type (GHashFunc    hash_func,
+                 GEqualFunc   key_equal_func,
+                 MonoGHashGCType type)
+{
+  MonoGHashTable *table = mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
+  if (type == MONO_HASH_KEY_GC || type == MONO_HASH_KEY_VALUE_GC)
+         g_assert (hash_func);
+  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) {
+         gsize bmap = 0;
+         if (type & MONO_HASH_KEY_GC)
+                 bmap |= 1; /* the first field in the node is the key */
+         if (type & MONO_HASH_VALUE_GC)
+                 bmap |= 2; /* the second is the value */
+         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;
+}
+
+
 
 /**
  * g_hash_table_new_full:
@@ -147,13 +200,13 @@ mono_g_hash_table_new_full (GHashFunc       hash_func,
                       GDestroyNotify  value_destroy_func)
 {
   MonoGHashTable *hash_table;
+#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;
   }
   
-#if HAVE_BOEHM_GC
   hash_table = GC_MALLOC (sizeof (MonoGHashTable));
 #else
   hash_table = g_new (MonoGHashTable, 1);
@@ -169,6 +222,7 @@ mono_g_hash_table_new_full (GHashFunc       hash_func,
 #else
   hash_table->nodes              = g_new0 (MonoGHashNode*, hash_table->size);
 #endif
+  hash_table->gc_type            = 0;
   
   return hash_table;
 }
@@ -191,12 +245,15 @@ mono_g_hash_table_destroy (MonoGHashTable *hash_table)
   g_return_if_fail (hash_table != NULL);
   
   for (i = 0; i < hash_table->size; i++)
-    g_hash_nodes_destroy (hash_table->nodes[i], 
+    g_hash_nodes_destroy (hash_table->nodes[i], hash_table->gc_type,
                          hash_table->key_destroy_func,
                          hash_table->value_destroy_func);
 
 #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
@@ -287,23 +344,66 @@ mono_g_hash_table_lookup_extended (MonoGHashTable    *hash_table,
 }
 
 static inline MonoGHashNode*
-g_hash_node_new (gpointer key,
-                gpointer value)
+g_hash_node_new (gint gc_type)
 {
   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_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) {
+         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);
 
-         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) {
+                 /* 
+                  * 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
+#ifdef _EGLIB_MAJOR
+  G_LOCK (g_hash_global);
+  if (node_free_list)
+    {
+      hash_node = node_free_list;
+      node_free_list = node_free_list->next;
+    }
+  else
+    {
+      hash_node = g_new0 (MonoGHashNode, 1);
+    }
+  G_UNLOCK (g_hash_global);
 #else
   G_LOCK (g_hash_global);
   if (node_free_list)
@@ -321,10 +421,11 @@ g_hash_node_new (gpointer key,
       hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
     }
   G_UNLOCK (g_hash_global);
+  #endif
 #endif
-  
-  hash_node->key = key;
-  hash_node->value = value;
+
+  hash_node->key = NULL;
+  hash_node->value = NULL;
   hash_node->next = NULL;
   
   return hash_node;
@@ -369,13 +470,16 @@ 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
     {
-      *node = g_hash_node_new (key, value);
-      hash_table->nnodes++;
-      G_HASH_TABLE_RESIZE (hash_table);
+           gint gc_type = hash_table->gc_type;
+           *node = g_hash_node_new (gc_type);
+           SET_NODE_KEY (*node, gc_type, key);
+           SET_NODE_VALUE (*node, gc_type, value);
+           hash_table->nnodes++;
+           G_HASH_TABLE_RESIZE (hash_table);
     }
 }
 
@@ -411,14 +515,17 @@ 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
     {
-      *node = g_hash_node_new (key, value);
-      hash_table->nnodes++;
-      G_HASH_TABLE_RESIZE (hash_table);
+           gint gc_type = hash_table->gc_type;
+           *node = g_hash_node_new (gc_type);
+           SET_NODE_KEY (*node, gc_type, key);
+           SET_NODE_VALUE (*node, gc_type, value);
+           hash_table->nnodes++;
+           G_HASH_TABLE_RESIZE (hash_table);
     }
 }
 
@@ -449,7 +556,7 @@ mono_g_hash_table_remove (MonoGHashTable       *hash_table,
     {
       dest = *node;
       (*node) = dest->next;
-      g_hash_node_destroy (dest, 
+      g_hash_node_destroy (dest, hash_table->gc_type,
                           hash_table->key_destroy_func,
                           hash_table->value_destroy_func);
       hash_table->nnodes--;
@@ -485,7 +592,7 @@ mono_g_hash_table_steal (MonoGHashTable    *hash_table,
     {
       dest = *node;
       (*node) = dest->next;
-      g_hash_node_destroy (dest, NULL, NULL);
+      g_hash_node_destroy (dest, hash_table->gc_type, NULL, NULL);
       hash_table->nnodes--;
   
       G_HASH_TABLE_RESIZE (hash_table);
@@ -571,7 +678,7 @@ g_hash_table_foreach_remove_or_steal (MonoGHashTable *hash_table,
              if (prev)
                {
                  prev->next = node->next;
-                 g_hash_node_destroy (node,
+                 g_hash_node_destroy (node, hash_table->gc_type,
                                       notify ? hash_table->key_destroy_func : NULL,
                                       notify ? hash_table->value_destroy_func : NULL);
                  node = prev;
@@ -579,7 +686,7 @@ g_hash_table_foreach_remove_or_steal (MonoGHashTable *hash_table,
              else
                {
                  hash_table->nodes[i] = node->next;
-                 g_hash_node_destroy (node,
+                 g_hash_node_destroy (node, hash_table->gc_type,
                                       notify ? hash_table->key_destroy_func : NULL,
                                       notify ? hash_table->value_destroy_func : NULL);
                  goto restart;
@@ -622,6 +729,23 @@ mono_g_hash_table_foreach (MonoGHashTable *hash_table,
       (* func) (node->key, node->value, user_data);
 }
 
+gpointer
+mono_g_hash_table_find (MonoGHashTable *hash_table, GHRFunc predicate, gpointer user_data)
+{
+       int i;
+       MonoGHashNode *node;
+       
+       g_return_val_if_fail (hash_table != NULL, NULL);
+       g_return_val_if_fail (predicate != NULL, NULL);
+
+       for (i = 0; i < hash_table->size; i++){
+               for (node = hash_table->nodes[i]; node; node = node->next)
+                       if ((*predicate)(node->key, node->value, user_data))
+                               return node->value;
+       }
+       return NULL;
+}
+
 /**
  * g_hash_table_size:
  * @hash_table: a #GHashTable.
@@ -658,8 +782,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
@@ -702,6 +828,7 @@ g_hash_table_resize (MonoGHashTable *hash_table)
 
 static void
 g_hash_node_destroy (MonoGHashNode      *hash_node,
+                    MonoGHashGCType type,
                     GDestroyNotify  key_destroy_func,
                     GDestroyNotify  value_destroy_func)
 {
@@ -714,13 +841,19 @@ g_hash_node_destroy (MonoGHashNode      *hash_node,
   hash_node->value = NULL;
 
   G_LOCK (g_hash_global);
+#if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
+  hash_node->next = node_free_lists [type];
+  node_free_lists [type] = hash_node;
+#else
   hash_node->next = node_free_list;
   node_free_list = hash_node;
+#endif
   G_UNLOCK (g_hash_global);
 }
 
 static void
 g_hash_nodes_destroy (MonoGHashNode *hash_node,
+                     MonoGHashGCType type,
                      GFreeFunc  key_destroy_func,
                      GFreeFunc  value_destroy_func)
 {
@@ -750,8 +883,51 @@ g_hash_nodes_destroy (MonoGHashNode *hash_node,
       node->value = NULL;
  
       G_LOCK (g_hash_global);
+#if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
+      node->next = node_free_lists [type];
+      node_free_lists [type] = hash_node;
+#else
       node->next = node_free_list;
       node_free_list = hash_node;
+#endif
       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)
+                                       node->key = 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)
+                                       node->value = 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)
+                                       node->key = mark_func (node->key);
+                               if (node->value)
+                                       node->value = mark_func (node->value);
+                       }
+               }
+       }
+}
+
+#endif