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 這樣的指令可能就要二個以上的低階組合語言指令.)
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 |
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 |
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
留言列表