in source/hack_parallel/hack_parallel/heap/hh_shared.c [1742:1793]
value hh_add(value key, value data) {
CAMLparam2(key, data);
check_should_exit();
uint64_t hash = get_hash(key);
unsigned int slot = hash & (hashtbl_size - 1);
unsigned int init_slot = slot;
while (1) {
uint64_t slot_hash = hashtbl[slot].hash;
if (slot_hash == hash) {
CAMLreturn(write_at(slot, data));
}
if (*hcounter >= hashtbl_size) {
// We're never going to find a spot
raise_hash_table_full();
}
if (slot_hash == 0) {
// We think we might have a free slot, try to atomically grab it.
if (__sync_bool_compare_and_swap(&(hashtbl[slot].hash), 0, hash)) {
uint64_t size = __sync_fetch_and_add(hcounter, 1);
// Sanity check
assert(size < hashtbl_size);
CAMLreturn(write_at(slot, data));
}
// Grabbing it failed -- why? If someone else is trying to insert
// the data we were about to, try to insert it ourselves too.
// Otherwise, keep going.
// Note that this read relies on the __sync call above preventing the
// compiler from caching the value read out of memory. (And of course
// isn't safe on any arch that requires memory barriers.)
if (hashtbl[slot].hash == hash) {
// Some other thread already grabbed this slot to write this
// key, but they might not have written the address (or even
// the sigil value) yet. We can't return from hh_add until we
// know that hh_mem would succeed, which is to say that addr is
// no longer null. To make sure hh_mem will work, we try
// writing the value ourselves; either we insert it ourselves or
// we know the address is now non-NULL.
CAMLreturn(write_at(slot, data));
}
}
slot = (slot + 1) & (hashtbl_size - 1);
if (slot == init_slot) {
// We're never going to find a spot
raise_hash_table_full();
}
}
}