# 2021q3 Homework2 (quiz2) contributed by < [`WayneLin1992`](https://github.com/WayneLin1992) > ###### tags: `linux2021` ## 延伸問題 - [ ] 解釋程式碼運作原理 - [ ] 指出改進空間並著手實作 - [ ] 對比 rcu_list,解釋同為 lock-free 演算法,跟上述 Hazard pointer 手法有何異同?能否指出 rcu_list 實作缺陷並改進? ## 解釋運作原理 `list_new` ```cpp list_t *list_new(void) { list_t *list = calloc(1, sizeof(*list)); assert(list); list_node_t *head = list_node_new(0), *tail = list_node_new(UINTPTR_MAX); assert(head), assert(tail); list_hp_t *hp = list_hp_new(3, __list_node_delete); atomic_init(&head->next, (uintptr_t) tail); *list = (list_t){.hp = hp}; atomic_init(&list->head, (uintptr_t) head); atomic_init(&list->tail, (uintptr_t) tail); return list; } ``` `calloc(size_t number, size_t size)` 配置一個空間,初始值為 0 `typedef struct list list_t` ```cpp typedef struct list { atomic_uintptr_t head, tail; list_hp_t *hp; } list_t; ``` `list_node_new` 就會配置一個 `list_node_t *node` 空間並且 aligned 128 ,`*node = (list_node_t){.magic = LIST_MAGIC, .key = key};`,後 atomic_add 使 `insert + 1` insert 為全域變數,所以**要注意是否有保持一致** `typedef struct list_node list_node_t` ```cpp typedef struct list_node { alignas(128) uint32_t magic; alignas(128) atomic_uintptr_t next; list_key_t key; } list_node_t; ``` 創立一個 tail 和 head `list_hp_new` 配置一個 `list_hp_t *hp` 空間並且 aligned 128 , `*hp = (list_hp_t){.max_hps = max_hps, .deletefunc = deletefunc};` 並創 hp[128] 個別要 `CLPAD * 2` 為 `32 bytes * sizeof(atomic_uintptr_t )` 的空間,hp->rl[128 * 32] 個別要 `sizeof(retirelist_t)` 的空間, `hp->rl[i * CLPAD]->list = calloc(HP_MAX_RETIRED, sizeof(uintptr_t));` `typedef struct list_hp list_hp_t` ```cpp struct list_hp { int max_hps; alignas(128) atomic_uintptr_t *hp[HP_MAX_THREADS]; alignas(128) retirelist_t *rl[HP_MAX_THREADS * CLPAD]; list_hp_deletefunc_t *deletefunc; }; ``` `retirelist_t` ```cpp typedef struct { int size; uintptr_t *list; } retirelist_t; ``` 回到 `list_new` 並將 list 中的 head、tail、head->next 設定好,並回傳。 ```cpp pthread_t thr[N_THREADS]; for (size_t i = 0; i < N_THREADS; i++) pthread_create(&thr[i], NULL, (i & 1) ? delete_thread : insert_thread, list); ``` 會檢查目前 i 為偶數就會 `insert_thread` 奇數就會 `delete_thread` 參數為 list。 ```cpp for (size_t i = 0; i < N_ELEMENTS; i++) { for (size_t j = 0; j < tid_v_base; j++) list_delete(list, (uintptr_t) &elements[j][i]); } ``` `list_delete` ```cpp bool list_delete(list_t *list, list_key_t key) { list_node_t *curr, *next; atomic_uintptr_t *prev; while (true) { if (!__list_find(list, &key, &prev, &curr, &next)) { list_hp_clear(list->hp); return false; } uintptr_t tmp = get_unmarked(next); if (!atomic_compare_exchange_strong(&curr->next, &tmp, get_marked(next))) continue; tmp = get_unmarked(curr); if (atomic_compare_exchange_strong(prev, &tmp, get_unmarked(next))) { list_hp_clear(list->hp); list_hp_retire(list->hp, get_unmarked(curr));//DDD } else { list_hp_clear(list->hp); } return true; } } ``` `__list_find` `__list_find(list, &key, &prev, &curr, &next)` 會透過 `curr = list->head` `next = curr->next` `prev = &list->head` `atomic_load(&get_unmarked_node(curr)->next) != (uintptr_t) next` 確保 next 沒被 mark `get_unmarked(next) == atomic_load((atomic_uintptr_t *) &list->tail)` 確保 next 不是 `&list->tail` `atomic_load(prev) != get_unmarked(curr)` 確保 curr 沒被 mark 與且 和 `prev` 和 `curr` 相等 `get_unmarked_node(next) == next` 確保 next 沒被 mark , `return get_unmarked_node(curr)->key == *key` 回傳 `curr->key == *key` 回到 `list_delete` `atomic_compare_exchange_strong(&curr->next, &tmp, get_marked(next))` 把 next mark 起來並存進 list 當中, mark **起來代表有人要處裡** `atomic_compare_exchange_strong(prev, &tmp, get_unmarked(next))` 把 `head = next` 並取消 next 的 mark `list_hp_clear` 將 `&hp->hp[tid()][i]` 存入 0 把原本的 curr 存入 retire list `rl->list[rl->size++] = ptr` `list_destroy` 會循環透過 `list_node_destroy` 刪除 node 之後並全域函數 `deletes + 1` 最後顯示出 `deletes` `inserts` 數值 ## 指出改進空間並著手實作 閱讀 [Lock-Free Data Structures with Hazard Pointers](https://erdani.org/publications/cuj-2004-12.pdf) 論文 才能實際了解到需要改進的地方