# 2021q3 Homework2 (hp)
contributed by < `blue76815` >
###### tags: `linux-summer-2021`
:::warning
注意共筆書寫規範:中英文間用一個半形空白字元區隔
:notes: jserv
:::
[第 2 週測驗題](https://hackmd.io/@sysprog/linux2021-summer-quiz2)
## To Do list
- [x] [Lock-Free 程式設計](https://hackmd.io/@sysprog/concurrency/https%3A%2F%2Fhackmd.io%2F%40sysprog%2Fconcurrency-lockfree)
- [x] [An Introduction to Lock-Free Programming](https://preshing.com/20120612/an-introduction-to-lock-free-programming/)
- [x] [Lock-Free Programming](https://www.cs.cmu.edu/~410-s05/lectures/L31_LockFree.pdf)
- [x] 閱讀論文 [Lock-Free Data Structures with Hazard Pointers](https://erdani.org/publications/cuj-2004-12.pdf)
- [ ] 閱讀論文 [A Pragmatic Implementation of Non-Blocking Linked Lists](https://www.cl.cam.ac.uk/research/srg/netos/papers/2001-caslists.pdf)
## 延伸問題:
- [ ] 解釋上述程式碼運作原理
- [ ] 指出改進空間並著手實作
- [ ] 對比 [rcu_list](https://github.com/sysprog21/concurrent-programs/tree/master/rcu_list),解釋同為 lock-free 演算法,跟上述 [Hazard pointer](https://en.wikipedia.org/wiki/Hazard_pointer) 手法有何異同?能否指出 [rcu_list](https://github.com/sysprog21/concurrent-programs/tree/master/rcu_list) 實作缺陷並改進?
## 3.解釋上述程式碼運作原理
從主程式開始
```c
#define N_THREADS (64) //NNN
int main(void)
{
list_t *list = list_new();
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);
//分離執行緒
for (size_t i = 0; i < N_THREADS; i++)
pthread_join(thr[i], NULL);
//刪除執行緒
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_destroy(list);
fprintf(stderr, "inserts = %zu, deletes = %zu\n", atomic_load(&inserts),
atomic_load(&deletes));
return 0;
}
```
主要介紹在
```c
#define N_THREADS (64) //NNN
for (size_t i = 0; i < N_THREADS; i++)
pthread_create(&thr[i], NULL, (i & 1) ? delete_thread : insert_thread,
list);
```
建立執行緒時,根據 `(i & 1) ? ` 奇偶數分配給
`delete_thread()`和`insert_thread()`函數
因為我們建立 `N_THREADS` (即 `64`) 個執行緒,
所以會有 32 個執行緒執行`delete_thread()`,32 個執行緒執行`insert_thread()`
### 3.2 `delete_thread()` 和 `insert_thread()` 函數
`insert_thread()` :用 list_insert() 插入 N_ELEMENTS(128) 筆資料
`delete_thread()` :用 list_delete() 刪除 N_ELEMENTS(128) 筆資料
```c
#define N_ELEMENTS 128
static void *insert_thread(void *arg)
{
list_t *list = (list_t *) arg;
for (size_t i = 0; i < N_ELEMENTS; i++)
(void) list_insert(list, (uintptr_t) &elements[tid()][i]);
return NULL;
}
bool list_insert(list_t *list, list_key_t key)
{
list_node_t *curr = NULL, *next = NULL;
atomic_uintptr_t *prev = NULL;
list_node_t *node = list_node_new(key);//新增節點(但是結構很特殊 待研究)
while (true) {
if (__list_find(list, &key, &prev, &curr, &next)) {//尋找節點資料然後插入 key 資料
//同時將節點的鄰居關係都更新
list_node_destroy(node);//釋放節點
list_hp_clear(list->hp);//清除 hazard point
return false;
}
// atomic 寫操作
// https://www.ibm.com/docs/en/zos/2.1.0?topic=c11-atomic-store-explicit
atomic_store_explicit(&node->next, (uintptr_t) curr,
memory_order_relaxed);
uintptr_t tmp = get_unmarked(curr);
//執行 atomic 比較和交換操作。
//若 prev 和 tmp 相同 則將 prev 資料儲存到node中
// https://www.ibm.com/docs/en/zos/2.1.0?topic=c11-atomic-compare-exchange-strong
if (atomic_compare_exchange_strong(prev, &tmp, (uintptr_t) node)) {
list_hp_clear(list->hp);//清除所有 hazard point
return true;
}
}
}
```
```c
#define N_ELEMENTS 128
static void *delete_thread(void *arg)
{
list_t *list = (list_t *) arg;
for (size_t i = 0; i < N_ELEMENTS; i++)
(void) list_delete(list, (uintptr_t) &elements[tid()][i]);
return NULL;
}
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);
//執行 atomic 比較和交換操作。
//若 prev 和 tmp 相同 則將 prev 資料儲存到 get_marked(next) 中
if (!atomic_compare_exchange_strong(&curr->next, &tmp,
get_marked(next)))
continue;
tmp = get_unmarked(curr);
//執行 Atomic 比較和交換操作。
//若 prev 和 tmp 相同 則將 prev 資料儲存到 get_unmarked(next) 中
if (atomic_compare_exchange_strong(prev, &tmp, get_unmarked(next))) {
list_hp_clear(list->hp);//清除所有 hazard point
list_hp_retire(list->hp, get_unmarked(curr)); //DDD
} else {
list_hp_clear(list->hp);//清除所有 hazard point
}
return true;
}
}
```
:::warning
不要將 "atomic" 翻譯為「原子」,因為現代漢語的紊亂
:notes: jserv
:::
### 3.3 `__list_find()`
在
```c
bool list_insert(list_t *list, list_key_t key)
{
.....
while (true) {
if (__list_find(list, &key, &prev, &curr, &next)) {//尋找節點資料然後插入 key
//同時將節點的鄰居關係都更新
..............
}
```
`__list_find(list, &key, &prev, &curr, &next)`
為尋找節點資料然後插入 key 資料
同時將節點的鄰居關係 `&prev` ,`&next` 都更新
對應的程式碼描述在
```c
static bool __list_find(list_t *list,
list_key_t *key,
atomic_uintptr_t **par_prev,
list_node_t **par_curr,
list_node_t **par_next)
{
while (true) {
........
if (get_unmarked_node(next) == next) { //跟資源釋放有關
if (!(get_unmarked_node(curr)->key < *key)) {
//檢查所在的節點 是在相鄰節點位址
*par_curr = curr;
*par_prev = prev;
*par_next = next;
return get_unmarked_node(curr)->key == *key; //FFF
}
prev = &get_unmarked_node(curr)->next;
(void) list_hp_protect_release(list->hp, HP_PREV,
get_unmarked(curr));
}
...............
}
*par_curr = curr;
*par_prev = prev;
*par_next = next;
return false;
}
```
---
## 4.指出改進空間並著手實作
---
## 5.對比 [rcu_list](https://github.com/sysprog21/concurrent-programs/tree/master/rcu_list),解釋同為 lock-free 演算法,跟上述 [Hazard pointer](https://en.wikipedia.org/wiki/Hazard_pointer) 手法有何異同?能否指出 [rcu_list](https://github.com/sysprog21/concurrent-programs/tree/master/rcu_list) 實作缺陷並改進?