Reentrant vs Thread-safe


Part 2: C 語言例子 (reentrant function)

先來看 reentrant 的例子: 最常見到用來說明 reentrant 的例子大概就屬 swap()

int t;

void swap(int *x, int *y) {
    t = *x;
    *x = *y;
    *y = t;
}

void isr(void) {
    int x = 1, y = 2;
    swap(&x, &y);
    ...
    return;
}

void main(void) {
    int x = 3, y = 4;
    ...
    swap(&x, &y);
    ...
}

這個例子中的 swap() 函數是 non-reentrant function (而且也不是 thread-save). 原因是第4行及第6行用到共用的變數 t. 即便是我們用的是 32bit CPU, 甚至也有技援 mem. to mem. 移轉資料不被中斷, 也還是有二個時間點(在第5行執行前或執行後) 發生中斷會產生錯誤的執行結果. 如下表的 NG Example 1 和 NG Example 2 所示 (在這裡為了讓這一張表格不致於太長, 我只把動作拆解到高階語言的單一指令階段, 實際上像 *x=*y 這樣的指令可能就要二個以上的低階組合語言指令.)

OK Example
main() isr() *x *y t
swap(); -
t = *x; 3 4 3
*x = *y; 4 4 3
*y = t; 4 3 3
swap(); 3
t = *x; 1 2 1
*x = *y; 2 2 1
*y = t; 2 1 1
NG Example 1
main() isr() *x *y t
swap(); -
t = *x; 3 4 3
swap(); 3
t = *x; 1 2 1
*x = *y; 2 2 1
*y = t; 2 1 1
*x = *y; 4 4 1
*y = t; 4 1 1
NG Example 2
main() isr() *x *y t
swap(); -
t = *x; 3 4 3
*x = *y; 4 4 3
swap(); 3
t = *x; 1 2 1
*x = *y; 2 2 1
*y = t; 2 1 1
*y = t; 4 1 1

上面的程式只要稍微修改一下, 把第1行搬到第3,4行中間. 也就是把變數 t 變成區域變數, 即可將 swap() 變成 reentrant function.

void swap(int *x, int *y) {
    int t;

    t = *x;
    *x = *y;
    *y = t;
}

void isr(void) {
    int x = 1, y = 2;
    swap(&x, &y);
    ...
    return;
}

void main(void) {
    int x = 3, y = 4;
    ...
    swap(&x, &y);
    ...
}

Wiki 網站則刻意把共用的變數 t 保留著, 加入一個區域變數 s, 一樣可以使 swap() 變成 reentrant function. (懷疑嗎? 可以仿照上面的例子畫一張表格填填看)

int t;

void swap(int *x, int *y) {
    int s;

    s = t;  // save global variable
    t = *x;
    *x = *y;
    *y = t;
    t = s;  // restore global variable
}

void isr(void) {
    int x = 1, y = 2;
    swap(&x, &y);
    ...
    return;
}

void main(void) {
    int x = 3, y = 4;
    ...
    swap(&x, &y);
    ...
}

這樣就 OK 了嗎? 不...

這是一個刻意設計用來作解說而非常不實用的例子, 因為 main()isr() 呼叫 swap() 所用的參數都是自己 local 變數. 一但 main()isr() 呼叫 swap() 所用的參數其中有一個是二者共用的變數, 整個程式的行為就會很奇怪:

  • main()isr() 呼叫 swap() 的次序不同結果不同 (邏輯上說不通)
  • swap() 又變成是 non-reentrant function 了 (試一下把變數 y 變成共用變數)

大家不可不慎.

這個 swap() 函數, 如果在不同的 thread 中呼叫它, 會有什麼結果? 一樣的如果傳入的參數都是單一個 thread 內部的區域變數那都是沒問題的. 但是, 只要呼叫 swap() 時傳入的參數當中有一個是這些 threads 之間共用的變數, 那 swap() 的 thread-save 也是破功. 那...加上個 mutex lock 和 unlock 吧? 不行吔! 用了 mutex 就不是 retrant function 了... 不能在 isr() 中呼叫.


後面還有 Part 3

    MagicJackTing 發表在 痞客邦 留言(0) 人氣()