# 2022q1 Homework5 (quiz6)
contributed by < `kdnvt` >
## Problem `2`
When putting new key/value pair in hash table, it replaces the key/value pair with the same key.
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
d [label="{ <data> k1 |<data> C | <ref> }"];
a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
d:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
d [label="{ <data> k1 |<data> C | <ref> }"];
a:ref:c -> d:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2, color=red];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
d:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
However, consider the situation which another thread delete the previous node right before the insertion of \<k1:c>.
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
z [label="{ <data> k0 |<data> Z | <ref> }"];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
d [label="{ <data> k1 |<data> C | <ref> }"];
a:ref:c -> z:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
z:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
d:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
z [label="{ <data> k0 |<data> Z | <ref> }"];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
d [label="{ <data> k1 |<data> C | <ref> }"];
a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2, color=red];
z:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
d:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
```graphviz
digraph foo {
rankdir=LR;
node [shape=record];
z [label="{ <data> k0 |<data> Z | <ref> }"];
a [label="{ <data> bucket[index] | <ref> }", width=1.2]
b [label="{ <data> k1 |<data> A | <ref> }"];
c [label="{ <data> k2 |<data> B | <ref> }"];
d [label="{ <data> k1 |<data> C | <ref> }"];
a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2 ];
z:ref:c -> d:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, color=red];
b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
d:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false];
}
```
The result will be wrong.
In addition to the bug, I think the code have many redundent part, so using the technique of indirect pointer to make it more concise.
```c
bool hashmap_put(hashmap_t *map, const void *key, void *value)
{
if (!map)
return NULL;
/* hash to convert key to a bucket index where value would be stored */
uint32_t bucket_index = map->hash(key) % map->n_buckets;
hashmap_kv_t *kv = NULL, **prev = NULL;
/* known head and next entry to add to the list */
hashmap_kv_t *head = NULL, *next = NULL;
printf("%p , %p\n",key,value);
while (true) {
/* copy the head of the list before checking entries for equality */
head = map->buckets[bucket_index];
/* find any existing matches to this key */
prev = &map->buckets[bucket_index];
if (head) {
for (kv = *prev; kv; kv = kv->next ) {
if (map->cmp(key, kv->key) == 0)
break;
prev = &(*prev)->next;
}
}
if (kv) { /* if the key exists, update and return it */
if (!next) /* lazy make the next key-value pair to append */
next = map->create_node(map->opaque, key, value);
/* ensure the linked list's existing node chain persists */
next->next = kv->next;
if (__atomic_compare_exchange(prev, &kv, &next, false,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST)) {
/* this node, key and value are never again used by this */
map->destroy_node(map->opaque, kv);
return true;
}
hashmap_put_replace_fail += 1;
} else { /* if the key does not exist, try adding it */
if (!next) /* make the next key-value pair to append */
next = map->create_node(map->opaque, key, value);
next->next = head;
if (__atomic_compare_exchange(&map->buckets[bucket_index], &head,
&next, false, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST)) {
__atomic_fetch_add(&map->length, 1, __ATOMIC_SEQ_CST);
return false;
}
/* failure means another thead updated head before this.
* track the CAS failure for tests -- non-atomic to minimize
* thread contention
*/
hashmap_put_retries += 1;
}
}
}
```
```c
bool hashmap_del(hashmap_t *map, const void *key)
{
if (!map)
return false;
uint32_t bucket_index = map->hash(key) % map->n_buckets;
/* try to find a match, loop in case a delete attempt fails */
while (true) {
hashmap_kv_t *match, **prev = &map->buckets[bucket_index];
for (match = *prev; match; match = match->next,prev = &(*prev)->next) {
if ((*map->cmp)(key, match->key) == 0)
break;
}
/* exit if no match was found */
if (!match)
return false;
/* previous means this not the head but a link in the list */
volatile uint32_t *fail = &map->buckets[bucket_index]==prev? &hashmap_del_fail_new_head:&hashmap_del_fail;
if (__atomic_compare_exchange(prev, &match, &match->next,
false, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST)) {
__atomic_fetch_sub(&map->length, 1, __ATOMIC_SEQ_CST);
map->destroy_node(map->opaque, match);
return true;
}
*fail += 1;
}
return false;
}
```