Reentrancy and Thread-safety
常常聽到看到 reentrancy 但老是跟 thread-safety 搞得不是太清楚。
Reentrancy
reentrancy 是 function 可以在執行過程中被中斷,在上一次執行還沒完成前可以再次進入這個 function 執行而不會有問題,也就是第二次進入 function 執行結束後,第一次的執行仍能正確完成。中斷可以是 jump 或 call,也可以是系統的 interrupt 或 signal。
reentrancy 是 single thread 環境下就有的概念。像是被系統 interrupt 中斷時 interrupt handler 是不是能夠再 call 剛剛執行到一半的 function?反過來說,interrupt handler 能 call 的只有 reentrant function。另外,recursive function 一般會是 reentrancy,但如果它使用了 global 變數就不一定。
reentrancy function 可以是 thread-safe 但不一定是 thread-safe。thread-safe function 可以是 reentrancy 也可以不是(繞口令時間)。function 是 thread-safe 但不是 reentrancy 的例子:
1 | function foo() |
在 single thread 的環境下,如果 foo()
執行到一半被 interrupt 打斷,interrupt handler 又 call foo()
會永遠等不到 mutex unlock,整個卡在那。
達成 reentrancy 的基本原則:
- 不使用 global(或 static)的變數
但如果執行過程確保 global 變數或 state 能在執行前後保持一致,是可以用的。 - 不修改自身的 code
- 不 call non-reentrant function
Thread-safety
thread-safety 是在 multiple thread 的環境下,thread 間的 shared data 可以在任意執行順序下依然保持正確。
達到 thread-safety 的方式分兩大類,一是避免 race condition,二是 synchronization。
- 避免 race condition
- 寫成不使用 global 變數的 reentrancy code
這種 reentrancy code 的寫法可以達成 thread-safety,但不是所有 reentrancy code 都是 thread-safety。反之,因為有很多達成 thread-safety 的方式,不是所有 thread-safety 都是 reentrancy。 - 使用 thread-local storage(TLS)
存在 TLS 裡的 data 是屬於某個 thread 的,不會在 thread 間共享。 - shared data 使用 immutable object
data 不會變當然就沒有 race condition 的問題啦!XD
- synchronization
- mutual exclusion 以及其變形們
要注意 dead-lock 或 resource starvation 等等問題 - atomic operation
確保 operation 不會被打斷的,通常有賴 instruction 層級的支援。高階 mutual exclusion 也是需要靠 atomic operation 實作的。
不同的 terminology
不同的 library、系統或語言可能自己定義 thread-safe 跟 reentrant。
例如 Qt 將 reentrant 以及 thread-safe 都定義在 multiple thread 的環境下。針對 class 來說,Qt 的 thread-safe class 是 multiple thread 可以 call 同一個 class instance 的 member function,而 reentrant class 則是 multiple thread 只要使用不同的 class instance 就能同時 call member function。