---
tags: linus2021
---
# 2021q1 Homework4 (quiz4)
contributed by < `yellow951321` >
> [第四周測驗題](https://hackmd.io/@sysprog/linux2021-quiz4)
## 程式原理解說
### `static struct __tpool_future *tpool_future_create(void)`
動態配置 `__tpool_future` 的結構體並且對成員做初始化
### `int tpool_future_destroy(struct __tpool_future *future)`
1. 首先確認傳入的 future 結構體是否存在
`if (future)`
2. 在刪除 future 物件時必須確保刪除的過程是連續的,不應該造成 race condition 因此先取得此 future 裡的 mutex lock 再接續做其他動作。
`pthread_mutex_lock(&future->mutex);`
3. 接著要確認目前的 thread 是處在必須要被移除的條件下,實現方式則是檢驗 future 結構體內的 `__future_flag` 的值
`if (future->flag & __FUTURE_FINISHED ||
future->flag & __FUTURE_CANCELLED)`
但這邊特別的是,雖然檢查了 `__FUTURE_FINISHED` 和 `__FUTURE_CANCELLED` ,但根據 `enum __future_flags` 的定義
```cpp
enum __future_flags {
__FUTURE_RUNNING = 01,
__FUTURE_FINISHED = 02,
__FUTURE_TIMEOUT = 04,
__FUTURE_CANCELLED = 010,
__FUTURE_DESTROYED = 020,
};
```
> 上述程式碼為八進位 :notes: jserv
因此以二進位表示為
```cpp
__FUTURE_FINISHED = 0b00010
__FUTURE_CANCELLED = 0b01010
```
這樣的話其實檢查 `__FUTURE_CANCELLED` 時就會檢查到 `__FUTURE_FINISHED` 。
:::warning
@TODO
找到這兩個 flag 的差異,去探討為何以二進位表示的這兩個值,如此設計的用意。
:::
4. 假使確認過是可以移除的狀態,則清除。否則先將 flag 與 `__FUTURE_DESTROYED` 做 bitwise AND 的運算。
```cpp
if (future->flag & __FUTURE_FINISHED ||
future->flag & __FUTURE_CANCELLED) {
pthread_mutex_unlock(&future->mutex);
pthread_mutex_destroy(&future->mutex);
pthread_cond_destroy(&future->cond_finished);
free(future);
} else {
future->flag |= __FUTURE_DESTROYED;
pthread_mutex_unlock(&future->mutex);
```
:::warning
@TODO
為何是對 `__FUTURE_DESTROYED` 做 bitwise AND 運算,做這個運算時同樣也是對 `__FUTURE_TIMEOUT` 做了 bitwise AND 運算, 這樣做有什麼意義。
> 後續的判斷就只需要 bitwise 操作,而非大量的列舉
> :notes: jserv
:::
### `void *tpool_future_get(struct __tpool_future *future, unsigned int seconds)`
1. 首先先拿到目前這個 future 的 mutex lock `pthread_mutex_lock(&future->mutex);`
2. 利用 bitwise operation 來重設 `__FUTURE_TIMEOUT` 的 flag
`future->flag &= ~__FUTURE_TIMEOUT;`
也就是將 flag 的第三個 bit (LSB) 設為 `0`
3. 首先檢查是否狀態為 finished ,如果是則回傳值。否的話則檢查 seconds 是否為 `0` ,如果不是的話進入`pthread_cond_timedwait(&future->cond_finished, &future->mutex, &expire_time);` 等待呼叫。接著等到 seconds 為 0 時,則直接利用 broadcast 喚醒 `pthread_cond_timedwait(&future->cond_finished, &future->mutex, &expire_time);` ,接著檢查是否因為 seconds 為 0 時被喚醒。如果是則將 flag 設定為 timeout,如果不是的話則會是因為完成 task 而被喚醒的,則會回傳 result。
4. FFF為 `pthread_cond_broadcast(&future->cond_finished, &future->mutex)`
```flow
st=>start: start
e=>end: return NULL
e2=>end: return future->result
op=>operation: future->flag & __FUTURE_FINISHED) == 0
op2=>operation: pthread_cond_timedwait()
op3=>operation: pthread_cond_broadcast(&future->cond_finished, &future->mutex)
cond=>condition: future->flag & __FUTURE_FINISHED) == 0
cond2=>condition: seconds == 0
cond3=>condition: timeout?
st->cond
cond(no)->e2
cond(yes)->cond2
cond2(yes)->op3->op2
cond2(no)->op2->cond3
cond3(yes)->e
cond3(no)->cond
```
### `static jobqueue_t *jobqueue_create(void)`
動態配置一個 jobqueue_t 的結構體,並初始化內部成員。
### `static void jobqueue_destroy(jobqueue_t *jobqueue)`
刪除傳入的整個 job_queue ,不過在刪除之前要先確保這個 job 是能夠刪除的。因此當 `future->flag` 不是 `finished` 或 `DESTROYED` 時。先將 flag 設定為 `__FUUTRE_CANCELLED` 。這樣當 `job_queue_fetch` 確認到這個 `job` 必須要被取消時,做了相對應的措施後,即可刪除。除此之外,因為 `while (tmp)` 的原因,這個函式必定會在刪除完整個 `jobqueue` 後才會結束。
### `static void __jobqueue_fetch_cleanup(void *arg)`
將傳入的 `void *` 強制轉型成 `pthread_mutex_t` 並且釋放出這個 lock 。
:::warning
@TODO
為什麼要特地寫一個函式出來做這件事
> 在 Linux 核心原始程式碼會大量看到這樣的程式設計風格,例如用 `__` 開頭的函式表示實作細節,而 `jobqueue_fetch_cleanup` (注意看開頭的 `jobqueue_` 表示公開程式碼介面),這樣的好處就是,當特定的實作要考慮不同平台和組態 (如 Arm vs. x86 和 Linux vs. macOS) 時,就可以存在多個 `__jobqueue_fetch_cleanup` 這樣的實作 (同名函式),但在 build system 指定不同的 C 程式碼,就可在編譯時期切換不同的平台和組態。
> :notes: jserv
:::