in src/heap/hh_shared.c [1621:1707]
CAMLprim value hh_add(value key, value addr) {
CAMLparam2(key, addr);
check_should_exit();
helt_t elt;
elt.hash = get_hash(key);
elt.addr = Long_val(addr);
size_t hashtbl_slots = info->hashtbl_slots;
unsigned int slot = elt.hash & (hashtbl_slots - 1);
unsigned int init_slot = slot;
while (1) {
uint64_t slot_hash = hashtbl[slot].hash;
if (slot_hash == elt.hash) {
// This value has already been been written to this slot, except that the
// value may have been deleted. Deleting a slot leaves the hash in place
// but replaces the addr field with NULL_ADDR. In this case, we can re-use
// the slot by writing the new address into.
//
// Remember that only reads and writes can happen concurrently, so we
// don't need to worry about concurrent deletes.
if (hashtbl[slot].addr == NULL_ADDR) {
// Two threads may be racing to write this value, so try to grab the
// slot atomically.
addr_t old_addr = __sync_val_compare_and_swap(
&hashtbl[slot].addr, NULL_ADDR, elt.addr);
if (old_addr == NULL_ADDR) {
__sync_fetch_and_add(&info->hcounter_filled, 1);
} else {
// Another thread won the race, but since the slot hashes match, it
// wrote the value we were trying to write, so our work is done.
addr = Val_long(old_addr);
}
}
break;
}
if (slot_hash == 0) {
helt_t old;
// This slot is free, but two threads may be racing to write to this slot,
// so try to grab the slot atomically. Note that this is a 16-byte CAS
// writing both the hash and the address at the same time. We expect the
// slot to contain `0`. If that's the case, `success == true`; otherwise,
// whatever data was in the slot at the time of the CAS will be stored in
// `old`.
old.value = 0;
_Bool success = __atomic_compare_exchange(
/* ptr */ &hashtbl[slot].value,
/* expected */ &old.value,
/* desired */ &elt.value,
/* weak */ 0,
/* success_memorder */ __ATOMIC_SEQ_CST,
/* failure_memorder */ __ATOMIC_SEQ_CST);
if (success) {
// The slot was still empty when we tried to CAS, meaning we
// successfully grabbed the slot.
uint64_t size = __sync_fetch_and_add(&info->hcounter, 1);
__sync_fetch_and_add(&info->hcounter_filled, 1);
assert(size < hashtbl_slots); // sanity check
break;
}
if (old.hash == elt.hash) {
// The slot was non-zero, so we failed to grab the slot. However, the
// thread which won the race wrote the value we were trying to write,
// meaning out work is done.
addr = Val_long(old.addr);
break;
}
}
slot = (slot + 1) & (hashtbl_slots - 1);
if (slot == init_slot) {
// We're never going to find a spot
raise_hash_table_full();
}
}
CAMLreturn(addr);
}