不同 thread 的 paths 呼叫同一 shared object 時,各 thread 對 shared object 存或 取的順序不同,將影響 shared object 的狀態,而導致執行結果之不同。如圖3-1 所示,path A1 與 B1 分屬不同 Run,且呼叫同一 shared object S1。若 A1 先執行存 取 S1 的 statement,且有寫入動作,則 A1 在寫入之前所執行指令的結果將影響 S1 的屬性值;當 path B1 從 S1 讀取資料時, B1 後續指令的執行結果將受到 A1 先前指令執行結果之影響,此可視為有一從 A1 經由 S1 流至 B1 的資料流 (data-flow),影響 B1 的執行結果。
圖3-1 兩條 path A1 與 B1 共同存取一個 shared object S1
若 A1 的輸入資料可經由 S1 流往 B1,而影響 B1 後半部分的執行。則稱 B1 的後半部執行結果依存於(dependents on)A1 的前半部的執行結果,或 A1 前半部 執行結果影響(influence)B1 後半部的結果。我們將這種因 shared object 資料流動 順序而造成影響的情形記為 A1 Æ B1。根據 A1 或 B1 何者先呼叫 S1,及 A1 或 B1 對 S1 之共用屬性變數作讀或寫的動作之不同,其相互影響關係如圖3-2所 示,由此表可知,當 A1 與 B1 對 S1 的共同屬性變數的動作同為讀取或同為寫入 時,A1 與 B1 間將無相互依存性;只有當一條 path 對 S1 共用屬性變數作讀取動 作,另一條為寫入動作,且寫入較讀取早發生時,A1 與 B1 才具有依存性。然 而若要知道各 path 對 shared object 的存取情況時,必需對所有 shared object 各個 method 的指令作分析,耗費甚多工夫,因此可假設存在 A1 Æ B1 及 B1 Æ A1。
先存取 S1 的 path 為 A1 或 B1 A1 的動作 B1 的動作 影響
A1 R R
A1 W R A1 Æ B1
A1 RW R A1 Æ B1
A1 R W
A1 W W
A1 RW W
A1 R RW
A1 W RW A1 Æ B1
A1 RW RW A1 Æ B1
B2 R R
B2 W R
A1 B1
S1
B2 RW R
B2 R W B1 Æ A1
B2 W W
B2 RW W B1 Æ A1
B2 R RW B1 Æ A1
B2 W RW
B2 RW RW B1 Æ A1
圖3-2 A1 與 B1 共同存取 S1 時的所有可能情況
Path 與 path 間也可能非因存取同一個 shared object 而產生依存性。一條 path A1 可能因為與另一個 Run 的 path B1 存取同一個 shared object S1 而有直接依存 性,使 B1 的資料影響了 A1 存取 S1 之後的行為;若 A1 在對 S1 的存取點之後,
又有一個對另一個 shared object S2 的存取點,而 A1 與另一 path C1 因 S2 而存在 直接依存性,進而影響 C1 執行的結果。在這樣的情形下,B1 的行為直接影響了 A1(B1 Æ A1),之後 A1 又直接影響了 C1(A1 Æ C1)。於是 B1 經由 A1 的執行而 間接影響 C1 的執行結果,稱之為間接依存性關係,記為 B1 Æ A1 Æ C1。Paths 會因為間接依存而互相影響執行結果,因此測試時也需被包含在一個 concurrent path 中。
Paths 間的依存關係應考慮每條 path 呼叫 shared object 指令的位置,才能正 確判斷其相互影響之資料流關係,先將每條 path 呼叫 shared object 的 statement 標出,以此為斷點而將 path 分為數個區段,稱為 path segment。
以圖3-3為例,一個擁有四個 Run 的系統共用三個 shared objects,其存取 情形如圖所示,任一 path 的號碼代表其呼叫 shared object 的位置。此圖中 Run A 之兩條 paths 與 Run B 之 path 呼叫一同 shared object S1,Run B 之 path 又與 Run C 呼叫同一 shared object S2,Run C 之 path 又與 Run D 之任一 path 呼叫同一 shared object S3,究竟 Run A 之 path 與 Run D 之 path 是否會相互影響呢?
圖3-3 存在間接相關性的系統範例
若 Run A 與 D 各選第一條 path 與 Run B 及 Run C 之 path 共組一 concurrent path,分別以 A1、B1、C1、D1 表示之。當 A1 之第 100 個 statement (記為 A1[100]) 對 S1 的呼叫將對共同屬性變數作寫入動作,而 B1[80]對呼叫 S1 則對此共用屬 性變數作讀取動作;而對於 S2,B1[130]有寫入共同屬性變數的動作,C1[150]
則有讀取共同屬性變數的動作;若 A1[100]較 B1[80]早執行,且 B1[130]較 C1[150]
早,則透過存取 S1,使 A1[100]將資料流往 B1[80] (A1[100] Æ B1[80]),同一條 path 中的 B1[80]將資料流往 B1[130] (B1[80]ÆB1[130]),再透過存取 S2 將資料 流往 C1[150] (B1[130] Æ C1[150]),因此會有一個資料流 A1[100] Æ B1[80] Æ B1[130] Æ C1[150],因此 A1,B1 及 C1 之關係可表示為 A1 Æ B1 Æ C1。
圖3-4 四條 paths 間的間接相關性
S1 S2
A1 B1 C1
80
130 150
S3
D1 35
100 90
Run A
100 100
Run B
80
Run C
150
Run D
90 90
S1 S2 S3
130
35
A1,B1,C1 與 D1 等四條 paths 之中所有可能的資料流包括(1) A1[100] Æ B1[80] Æ B1[130] Æ C1[150],(2) B1[80] Æ A1[100],(3) C1[150] Æ B1[130],(4) C1[35] Æ D1[90],與(5) D1[90] Æ C1[35] Æ C1[150] Æ B1[130];因此其依存性 關係為(1) A1 Æ B1 Æ C1,(2) B1 Æ A1,(3) C1 Æ B1,(4) C1 Æ D1,(5) D1 Æ C1 Æ B1。換言之, A1 與 D1 皆不會影響彼此的執行。有依存關係之 path 組合需 被測試,才能驗證其交互行為之正確性。
依存性 A1 Æ B1 Æ C1 之交互行為若用包含 A1B1C1 這三條 paths 的 concurrent path 來測試,但此 concurrent path 也可同時測試 B1 Æ A1 與 C1 Æ B1 這兩個依存性。同理,依存性 D1 Æ C1 Æ B1 需以包含 B1C1D1 這三條 paths 的 concurrent path 來測試,且這條 concurrent path 亦可同時測試 C1 Æ D1 的依存 性。由此可知,需產生包含 A1B1C1 與 B1C1D1 此二組 path 集合的 concurrent paths 來測試。
在圖3-3中,A1 及 A1 與 D1 及 D2 相互間不會影響彼此的執行,因此只需 選出包含 A1B1C1、B1C1D1、A2B1C1 與 B1C1D2 這四組 path 集合的 concurrent paths 來測試。選擇包含此四個 Run 的 concurrent path A1B1C1D1 與 A2B1C1D2,
即可驗證所有 paths 間互相影響的執行行為。
由上例可知,由不圖 path 間之資料流可找出有相互依存關係的不同 Run 之 path 組合,這些 path 組合一定要被測試,因此要找出最少數目的 concurrent path 來測試由一條 main()的 path 所呼叫並行執行的 Run 的所有行為,必需先找出有 依存關係之 path 組合後,才能找出滿足(1)所有 path 皆可被測試及(2)所有有依存 關係之 path 組合皆可被測試的並行路徑組合。尋找 path 組合之方法將於3.2節 敘述,產生 concurrent path 的方法將於3.3節敘述。