--- 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 :::