in source/hack_parallel/hack_parallel/heap/hh_shared.c [1336:1392]
static void prepend_to_deptbl_list(uint32_t key, uint32_t val) {
volatile deptbl_entry_t* const table = deptbl;
size_t slot = 0;
for (slot = (size_t)hash_uint64(key);; ++slot) {
slot &= dep_size - 1;
deptbl_entry_t slotval = table[slot];
if (slotval.raw == 0) {
// Slot is empty. Try to create a new linked list head here.
deptbl_entry_t head = {{{key, TAG_KEY}, {val, TAG_VAL}}};
slotval.raw = __sync_val_compare_and_swap(&table[slot].raw, 0, head.raw);
if (slotval.raw == 0) {
// The CAS succeeded, we are done.
break;
}
// slotval now holds whatever some racing writer put there.
}
if (slotval.s.key.num == key && slotval.s.key.tag == TAG_KEY) {
// A list for this key already exists. Prepend to it by chaining
// our new linked list node to whatever the head already points to
// then making the head point to our node.
//
// The head can of course change if someone else prepends first, in
// which case we'll retry. This is just the classic "atomic push onto
// linked list stock" algarithm.
// Allocate a linked list node to prepend to the list.
uint32_t list_slot = alloc_deptbl_node(key, val);
// The new head is going to point to our node as the first entry.
deptbl_entry_t head = {{{key, TAG_KEY}, {list_slot, TAG_NEXT}}};
while (1) {
// Update our new linked list node, which no one can see yet, to
// point to the current list head.
table[list_slot].s.next = slotval.s.next;
// Try to atomically set the new list head to be our node.
uint64_t old = slotval.raw;
slotval.raw =
__sync_val_compare_and_swap(&table[slot].raw, old, head.raw);
if (slotval.raw == old) {
// The CAS succeeded, we are done.
break;
}
}
break;
}
}
}