/** * mono_conc_hashtable_insert * * @Returns the old value if key is already present or null */ gpointer mono_conc_hashtable_insert (MonoConcurrentHashTable *hash_table, gpointer key, gpointer value) { conc_table *table; key_value_pair *kvs; int hash, i, table_mask; g_assert (key != NULL && key != TOMBSTONE); g_assert (value != NULL); hash = mix_hash (hash_table->hash_func (key)); mono_mutex_lock (hash_table->mutex); if (hash_table->element_count >= hash_table->overflow_count) expand_table (hash_table); table = (conc_table*)hash_table->table; kvs = table->kvs; table_mask = table->table_size - 1; i = hash & table_mask; if (!hash_table->equal_func) { for (;;) { if (!kvs [i].key || kvs [i].key == TOMBSTONE) { kvs [i].value = value; /* The write to values must happen after the write to keys */ mono_memory_barrier (); kvs [i].key = key; ++hash_table->element_count; mono_mutex_unlock (hash_table->mutex); return NULL; } if (key == kvs [i].key) { gpointer value = kvs [i].value; mono_mutex_unlock (hash_table->mutex); return value; } i = (i + 1) & table_mask; } } else { GEqualFunc equal = hash_table->equal_func; for (;;) { if (!kvs [i].key || kvs [i].key == TOMBSTONE) { kvs [i].value = value; /* The write to values must happen after the write to keys */ mono_memory_barrier (); kvs [i].key = key; ++hash_table->element_count; mono_mutex_unlock (hash_table->mutex); return NULL; } if (equal (key, kvs [i].key)) { gpointer value = kvs [i].value; mono_mutex_unlock (hash_table->mutex); return value; } i = (i + 1) & table_mask; } } }
gpointer mono_conc_hashtable_lookup (MonoConcurrentHashTable *hash_table, gpointer key) { MonoThreadHazardPointers* hp; conc_table *table; int hash, i, table_mask; key_value_pair *kvs; hash = mix_hash (hash_table->hash_func (key)); hp = mono_hazard_pointer_get (); retry: table = (conc_table *)get_hazardous_pointer ((gpointer volatile*)&hash_table->table, hp, 0); table_mask = table->table_size - 1; kvs = table->kvs; i = hash & table_mask; if (G_LIKELY (!hash_table->equal_func)) { while (kvs [i].key) { if (key == kvs [i].key) { gpointer value; /* The read of keys must happen before the read of values */ mono_memory_barrier (); value = kvs [i].value; /* FIXME check for NULL if we add suppport for removal */ mono_hazard_pointer_clear (hp, 0); return value; } i = (i + 1) & table_mask; } } else { GEqualFunc equal = hash_table->equal_func; while (kvs [i].key) { if (kvs [i].key != TOMBSTONE && equal (key, kvs [i].key)) { gpointer value; /* The read of keys must happen before the read of values */ mono_memory_barrier (); value = kvs [i].value; /* We just read a value been deleted, try again. */ if (G_UNLIKELY (!value)) goto retry; mono_hazard_pointer_clear (hp, 0); return value; } i = (i + 1) & table_mask; } } /* The table might have expanded and the value is now on the newer table */ mono_memory_barrier (); if (hash_table->table != table) goto retry; mono_hazard_pointer_clear (hp, 0); return NULL; }
static MONO_ALWAYS_INLINE void insert_one_local (conc_table *table, GHashFunc hash_func, gpointer key, gpointer value) { key_value_pair *kvs = table->kvs; int table_mask = table->table_size - 1; int hash = mix_hash (hash_func (key)); int i = hash & table_mask; while (table->kvs [i].key) i = (i + 1) & table_mask; kvs [i].key = key; kvs [i].value = value; }
static ColorData* find_in_cache (int *insert_index) { HashEntry *bucket; int i, hash, size, index; size = dyn_array_ptr_size (&color_merge_array); /* Cache checking is very ineficient with a lot of elements*/ if (size > 3) return NULL; hash = 0; for (i = 0 ; i < size; ++i) hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i)); if (!hash) hash = 1; index = hash & (COLOR_CACHE_SIZE - 1); bucket = merge_cache [index]; for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) { if (bucket [i].hash != hash) continue; if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) { ++cache_hits; return bucket [i].color; } } //move elements to the back for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i) bucket [i] = bucket [i - 1]; ++cache_misses; *insert_index = index; bucket [0].hash = hash; return NULL; }
/** * mono_conc_hashtable_remove: * * Remove a value from the hashtable. Requires external locking * * @Returns the old value if key is already present or null */ gpointer mono_conc_hashtable_remove (MonoConcurrentHashTable *hash_table, gpointer key) { conc_table *table; key_value_pair *kvs; int hash, i, table_mask; g_assert (key != NULL && key != TOMBSTONE); hash = mix_hash (hash_table->hash_func (key)); table = (conc_table*)hash_table->table; kvs = table->kvs; table_mask = table->table_size - 1; i = hash & table_mask; if (!hash_table->equal_func) { for (;;) { if (!kvs [i].key) { return NULL; /*key not found*/ } if (key == kvs [i].key) { gpointer value = kvs [i].value; kvs [i].value = NULL; mono_memory_barrier (); kvs [i].key = TOMBSTONE; if (hash_table->key_destroy_func != NULL) (*hash_table->key_destroy_func) (key); if (hash_table->value_destroy_func != NULL) (*hash_table->value_destroy_func) (value); return value; } i = (i + 1) & table_mask; } } else { GEqualFunc equal = hash_table->equal_func; for (;;) { if (!kvs [i].key) { return NULL; /*key not found*/ } if (kvs [i].key != TOMBSTONE && equal (key, kvs [i].key)) { gpointer old_key = kvs [i].key; gpointer value = kvs [i].value; kvs [i].value = NULL; mono_memory_barrier (); kvs [i].key = TOMBSTONE; if (hash_table->key_destroy_func != NULL) (*hash_table->key_destroy_func) (old_key); if (hash_table->value_destroy_func != NULL) (*hash_table->value_destroy_func) (value); return value; } i = (i + 1) & table_mask; } } }