#endif
#include <glib.h>
-#if HAVE_BOEHM_GC
-#include <gc/gc.h>
-#endif
#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
GEqualFunc key_equal_func;
GDestroyNotify key_destroy_func;
GDestroyNotify value_destroy_func;
+ MonoGHashGCType gc_type;
};
#define G_HASH_TABLE_RESIZE(hash_table) \
static MonoGHashNode** g_hash_table_lookup_node (MonoGHashTable *hash_table,
gconstpointer key);
static MonoGHashNode* g_hash_node_new (gpointer key,
- gpointer value);
+ gpointer value,
+ 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,
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;
+#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
+#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
/**
* g_hash_table_new:
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);
+ 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:
GDestroyNotify value_destroy_func)
{
MonoGHashTable *hash_table;
-
#if HAVE_BOEHM_GC
- hash_table = GC_malloc (sizeof (MonoGHashTable));
+ static gboolean inited = FALSE;
+ if (!inited) {
+ MONO_GC_REGISTER_ROOT (node_free_lists [0]);
+ inited = TRUE;
+ }
+
+ hash_table = GC_MALLOC (sizeof (MonoGHashTable));
#else
hash_table = g_new (MonoGHashTable, 1);
#endif
hash_table->size = HASH_TABLE_MIN_SIZE;
hash_table->nnodes = 0;
hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
- hash_table->key_equal_func = key_equal_func;
+ hash_table->key_equal_func = key_equal_func == g_direct_equal ? NULL : key_equal_func;
hash_table->key_destroy_func = key_destroy_func;
hash_table->value_destroy_func = value_destroy_func;
#if HAVE_BOEHM_GC
- hash_table->nodes = GC_malloc (sizeof (MonoGHashNode*) * hash_table->size);
+ hash_table->nodes = GC_MALLOC (sizeof (MonoGHashNode*) * hash_table->size);
#else
hash_table->nodes = g_new0 (MonoGHashNode*, hash_table->size);
#endif
+ hash_table->gc_type = 0;
return 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
return FALSE;
}
+static inline MonoGHashNode*
+g_hash_node_new (gpointer key,
+ gpointer value,
+ gint gc_type)
+{
+ MonoGHashNode *hash_node = NULL;
+
+#if HAVE_BOEHM_GC
+ 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_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) {
+ /*
+ * 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)
+ {
+ hash_node = node_free_list;
+ node_free_list = node_free_list->next;
+ }
+ else
+ {
+ 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);
+ }
+ G_UNLOCK (g_hash_global);
+#endif
+
+ SET_NODE_KEY (hash_node, gc_type, key);
+ SET_NODE_VALUE (hash_node, gc_type, value);
+ hash_node->next = NULL;
+
+ return hash_node;
+}
+
/**
* g_hash_table_insert:
* @hash_table: a #GHashTable.
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);
+ *node = g_hash_node_new (key, value, hash_table->gc_type);
hash_table->nnodes++;
G_HASH_TABLE_RESIZE (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);
+ *node = g_hash_node_new (key, value, hash_table->gc_type);
hash_table->nnodes++;
G_HASH_TABLE_RESIZE (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--;
{
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);
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;
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;
return hash_table->nnodes;
}
+/**
+ * mono_g_hash_table_remap:
+ *
+ * Calls the given function for each key-value pair in the hash table,
+ * and replaces the value stored in the hash table by the value returned by
+ * the function.
+ *
+ **/
+void
+mono_g_hash_table_remap (MonoGHashTable *hash_table,
+ MonoGRemapperFunc func,
+ gpointer user_data)
+{
+ MonoGHashNode *node;
+ gint i;
+
+ g_return_if_fail (hash_table != NULL);
+ g_return_if_fail (func != NULL);
+
+ for (i = 0; i < hash_table->size; i++)
+ 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
g_hash_table_resize (MonoGHashTable *hash_table)
{
new_size = CLAMP (new_size, HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE);
#if HAVE_BOEHM_GC
- new_nodes = GC_malloc (sizeof (MonoGHashNode*) * new_size);
+ new_nodes = GC_MALLOC (sizeof (MonoGHashNode*) * new_size);
#else
new_nodes = g_new0 (MonoGHashNode*, new_size);
#endif
hash_table->size = new_size;
}
-static MonoGHashNode*
-g_hash_node_new (gpointer key,
- gpointer value)
-{
- MonoGHashNode *hash_node;
-
- G_LOCK (g_hash_global);
- if (node_free_list)
- {
- hash_node = node_free_list;
- node_free_list = node_free_list->next;
- }
- else
- {
-#if HAVE_BOEHM_GC
- hash_node = GC_malloc (sizeof (MonoGHashNode));
-#else
- 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);
-#endif
- }
- G_UNLOCK (g_hash_global);
-
- hash_node->key = key;
- hash_node->value = value;
- hash_node->next = NULL;
-
- return hash_node;
-}
-
static void
g_hash_node_destroy (MonoGHashNode *hash_node,
+ MonoGHashGCType type,
GDestroyNotify key_destroy_func,
GDestroyNotify value_destroy_func)
{
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)
{
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)
+ 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