# 2023 Homework2 (quiz1) > contributed by < Eddie-064 > > [Github](https://github.com/Eddie-064/linux2023-summer) ## 程式碼運作原理 (待捕) - Makefile: 這邊定義了兩個 Flag,USE_PTHREADS 與 USE_LINUX,make check 後會分別編譯並執行這兩個程式。 #### `futex` 這邊封裝了三種需要系統呼叫的 futex 操作。 使用 futex lock 相較於使用 pthread mutex lock 較低成本,futex 是在 user space 使用 atomic 指令透過硬體完成最小操作。 :::info condvar 與 broadcast 概念 當對多個 condvar 進行 broadcast 時,多個等待 condvar 的執行緒回到用戶空間競爭所屬的 mutex,而每個執行緒又因競爭 mutex 回到 kernel 排隊等待,這樣多次的切換使用系統呼叫,容易造成系統資源浪費。 - condvar waiter 在 kernel 等待被喚醒 ![](https://hackmd.io/_uploads/HyKeoBIah.png) ::: - `futex_requeue`: 看到上面的圖後才看懂,教材 futex_requeue 的圖,futex_requeue 就是避免多個執行緒被喚醒回到用戶空間,又因競爭 mutex 而再次回到 kernel,直接在 kernel 中移到另一個佇列等待 mutex 喚醒。 ```c /* Atomically check if '*futex == value', and if so, go to sleep */ static inline void futex_wait(atomic int *futex, int value) { syscall(SYS_futex, futex, FUTEX_WAIT_PRIVATE, value, NULL); } /* Wake up 'limit' threads currently waiting on 'futex' */ static inline void futex_wake(atomic int *futex, int limit) { syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, limit); } /* Wake up 'limit' waiters, and re-queue the rest onto a different futex */ static inline void futex_requeue(atomic int *futex, int limit, atomic int *other) { syscall(SYS_futex, futex, FUTEX_REQUEUE_PRIVATE, limit, INT_MAX, other); } ``` #### `mutex` 這邊定義 `mutex_t` 結構,變數 `state` 宣告為 `atomic` 確保在多執行緒環境下變數最小操作,也就是只有完成操作與沒有操作,不會有其他中間狀態。 ```c typedef struct { atomic int state; } mutex_t; enum { MUTEX_LOCKED = 1 << 0, MUTEX_SLEEPING = 1 << 1, }; ``` - `mutex_trylock` : 這邊首先用 atomic `load` 讀取確認 lock 狀態是不是 locked,如果沒有其他人使用則透過 `fetch_or` 寫入 `MUTEX_LOCKED`。`thread_fence` 用於保護 atomic 操作的前後順序不會因編譯器或 CPU 的優化而更動。 ```c static bool mutex_trylock(mutex_t *mutex) { int state = load(&mutex->state, relaxed); if (state & MUTEX_LOCKED) return false; state = fetch_or(&mutex->state, MUTEX_LOCKED, relaxed); if (state & MUTEX_LOCKED) return false; thread_fence(&mutex->state, acquire); return true; } ``` - `mutex_lock`: 待補,`__builtin_ia32_pause` 會節省資源的消耗,但不知道和 `futex_wait` 實作的差異。 ```c static inline void mutex_lock(mutex_t *mutex) { #define MUTEX_SPINS 128 for (int i = 0; i < MUTEX_SPINS; ++i) { if (mutex_trylock(mutex)) return; spin_hint(); } int state = exchange(&mutex->state, MUTEX_LOCKED | MUTEX_SLEEPING, relaxed); while (state & MUTEX_LOCKED) { futex_wait(&mutex->state, MUTEX_LOCKED | MUTEX_SLEEPING); state = exchange(&mutex->state, MUTEX_LOCKED | MUTEX_SLEEPING, relaxed); } thread_fence(&mutex->state, acquire); } ``` - `mutex_unlock`: 透過 `exchange` 的 atomic 操作讀取值後寫入 0,如果原先狀態是 `MUTEX_SLEEPING` 則需要被喚醒。 ```c static inline void mutex_unlock(mutex_t *mutex) { int state = exchange(&mutex->state, 0, release); if (state & MUTEX_SLEEPING) futex_wake(&mutex->state, 1); // FFFF } ``` #### condvar - 包含 `cond_wait`、`cond_signal`、`cond_broadcast` 三種操作。 #### main ``` struct clock { mutex_t mutex; cond_t cond; int ticks; }; ``` ### 測試方法 ### futex 使用方式 ### POSIX Threads 強化哪些相關 futex 實作機制 ## 修改並行快速排序實作 futex 版本 ## 於 POSIX Thread 實作 priority inheritance mutex ## skinny-mutex POSIX Threads and Performance