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
2
3
4
5
6
function foo()
{
mutex_lock();
//blah...
mutex_unlock();
}

在 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。

  1. 避免 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
  1. 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。