例題2
購入者、自動販売機をそれぞれCSPでモデリングし、正しく動作するシステムを検証しながら設計してみよう。まず、下記のような購入者3人と自動販売機をモデリングする。
購入者の動作はコインを入れ、ジュースのボタンを押す。
channel coin, juice PERSON = coin -> juice -> PERSON
プロセスPEOPLE は3人のPERSON から成り、個別に自販機でジュースを購入する。
PEOPLE = |||i:{1..3}@ PERSON -- = PERSON ||| PERSON ||| PERSON と同じ。
自動販売機はジュースの残数 m 、入れられたコインの枚数 n を保持する。ジュースが買えるのは m>0 かつ n>0 のときであり、コインをいれれるのは n<5 かつ m>0 のときとする
VM(m,n) = (n<5)and(m>0)&coin -> VM(m,n+1) [] (n>0)and(m>0)&juice -> VM(m-1,n-1)
自販機にジュースが5本残っている状態として、3人の購入者それぞれと自販機はイベントcoin,juice で同期する。
SYSTEM = PEOPLE[{coin,juice}||{coin,juice}]VM(5,0)
この SYSTEM は人々がジュースを買うごとに残数 m が減っていき、0になると自販機が juiceイベントを起こせなくなるので止まってしまう。
FDR2のデッドロックのデバック画面からもわかる。
プロセスSYSTEMはcoin,juiceを5回繰り返すとAcceptの欄が空になり、起こせるイベントがなくなっている。
そこで、ジュースを補充する業者(VENDOR)も考えることにする。
業者は自販機にジュースを補充(fill)する。いつ補充するかは決まっていないが、補充は自販機に入っているコインの枚数が0であることを確認してから行う。
channel fill VENDOR = fill -> VENDOR VM(m,n) = (n<5)and(m>0)&coin -> VM(m,n+1) [] (n>0)and(m>0)&juice -> VM(m-1,n-1) [] (n==0)&fill -> VM(10,0) --残数を10本にする。 SYSTEM = PEOPLE [{coin,juice}||{coin,juice}] ( VM(5,0) [{coin,juice,fill}||{fill}] VENDOR \ {fill} )
人々がジュースを買うごとに残数が減っていくが、たとえ0になっても業者がときどき補充してくれるので、今度は止まることなく動き続ける。だろうか?
FDR2で検証すると、まだデッドロックになるケースがあることがわかる。
デバック画面を見ると確かにプロセスVMの起こせるイベントがなくなっている。
原因は、プロセスPERSONがそれぞれ独立に動くので1人がコインを入れた後ジュースを買う前に別のもう1人がコインを入れることができる。
プロセスVMも連続してコインを入れることは可能としているが、ジュースの残数を超えてコインを入れてしまうと、残数が0(m=0)になったときにコインが残っており(n>0)、補充できなくなって止まってしまうのである。
そこで返却レバーを加えることにすると、止まることなく動作するシステムが設計できる。
channel coin, juice, fill, return PEOPLE = |||i:{1..3}@ PERSON PERSON = coin -> ( juice -> PERSON [] return -> PERSON ) --returnを入れる。 VM(m,n) = (n<5)and(m>0)&coin -> VM(m,n+1) [] (n>0)and(m>0)&juice -> VM(m-1,n-1) [] (n==0)&fill -> VM(10,0) [] return -> VM(m,0) --コインを0にする。 VENDOR = fill -> VENDOR SYSTEM = PEOPLE --アルファベットに注意。 [{coin,juice,return}||{coin,juice,return}] ( VM(5,0) [{coin,juice,fill,return}||{fill}] VENDOR \ {fill} )