2017-11-03 7 views
1

これでPromelaでCLH-RW lockをモデル化しようとしています。プロメラのキューをモデル化する方法は?

ロックの仕組みはシンプルです、本当に:

キューは、リーダーとライターの両方が、新しいノードとCAS-INGを作成することによって、彼らがそうする単一bool succ_must_waitを含むノードをエンキューするtail、で構成されていそれはtailです。

これにより、テールがノードの先行ノードpredになります。

次に、でスピンウェイトがfalseになるまで待ちます。

リーダーは、最初にリーダーカウンタncritRをインクリメントしてから、自分のフラグをfalseに設定し、同時にクリティカルセクションの複数のリーダーを許可します。読み取りロックを解除するとは、単にncritRをデクリメントすることを意味します。

ライターはncritRがゼロになるまで待ってから、クリティカルセクションに入ります。ロックが解除されるまで、フラグはfalseに設定されません。

私はプロメラでこれをモデル化するのに苦労しています。

私の現在の試み(下記参照)は、配列を使用しようとします。各ノードは、基本的にいくつかの配列エントリで構成されています。

これは、Aがエンキューしたとすると、B自体がエンキューするため、失敗します。その後、キューは次のようになります。Sは、センチネルリンパ節をある

S <- A <- B 

を。今

問題がときA完全性実行され、再エンキュー、キューは実際の実行で

S <- A <- B <- A' 

のように見えるだろうということ、であるAA'が別個のノードオブジェクトであるため、これは絶対に大丈夫です。 Aが最初にロックを解除したときにA.succ_must_waitfalseに設定されているため、最終的にはBが処理を進めるため、最終的に処理が進まなくなります。

以下のアレイベースのPROMELAモデルでは何が起こる、しかし、誤って(AA'が、それによってBがあるデッドロックを作成し、Aがロックをリリースしたことを欠場するBを引き起こして、同じ配列位置を占めていることです)A'の代わりにAA'が待っています(正確にはB)。

この可能性のある「解決策」は、がBが承認を待つまで待つことができます。しかし、それはロックの仕組みには当てはまりません。

もう1つの解決策は、変更が0にリセットされるのではなく、succ_must_waitが増分されるpred.succ_must_waitの変更を待つことです。

しかし、増加バージョンwouldnのようなものを私は(つまり、ノードは、前任者の一部を無視させることができる場合)predが変更される可能性があり、ロックのバージョンをモデル化しようとするんだ、と私は完全に確信していませんこの変更の問題を引き起こしません。

プロメラで暗黙のキューをモデル化する "賢明な"方法は何でしょうか?すべての

/* CLH-RW Lock */ 
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/ 

ltl liveness{ 
    ([]<> reader[1]@progress_reader) 
    && ([]<> reader[2]@progress_reader) 
    && ([]<> writer[3]@progress_writer) 
    && ([]<> writer[4]@progress_writer) 
} 

bool initialised = 0; 

byte ncritR; 
byte ncritW; 
byte tail; 
bool succ_must_wait[5] 
byte pred[5] 

init{ 
    assert(_pid == 0); 
    ncritR = 0; 
    ncritW = 0; 

    /*sentinel node*/ 
    tail =0; 
    pred[0] = 0; 
    succ_must_wait[0] = 0; 
    initialised = 1; 
} 

active [2] proctype reader() 
{ 
    assert(_pid >= 1); 
    (initialised == 1) 
    do 
    :: else -> 
     succ_must_wait[_pid] = 1; 
     atomic { 
      pred[_pid] = tail; 
      tail = _pid; 
     } 

     (succ_must_wait[pred[_pid]] == 0) 

     ncritR++; 
     succ_must_wait[_pid] = 0; 
     atomic { 
      /*freeing previous node for garbage collection*/ 
      pred[_pid] = 0; 
     } 

     /*CRITICAL SECTION*/ 
progress_reader: 
     assert(ncritR >= 1); 
     assert(ncritW == 0); 

     ncritR--; 

     atomic { 
      /*necessary to model the fact that the next access creates a new queue node*/ 
      if 
      :: tail == _pid -> tail = 0; 
      :: else -> 
      fi 
     } 
    od 
} 

active [2] proctype writer() 
{ 
    assert(_pid >= 1); 
    (initialised == 1) 
    do 
    :: else -> 
     succ_must_wait[_pid] = 1; 

     atomic { 
      pred[_pid] = tail; 
      tail = _pid; 
     } 

     (succ_must_wait[pred[_pid]] == 0) 
     (ncritR == 0) 
     atomic { 
      /*freeing previous node for garbage collection*/ 
      pred[_pid] = 0; 
     } 
     ncritW++; 

     /* CRITICAL SECTION */ 
progress_writer: 
     assert(ncritR == 0); 
     assert(ncritW == 1); 

     ncritW--; 
     succ_must_wait[_pid] = 0; 

     atomic { 
      /*necessary to model the fact that the next access creates a new queue node*/ 
      if 
      :: tail == _pid -> tail = 0; 
      :: else -> 
      fi 
     } 
    od 
} 

答えて

1

まず、いくつかの注意事項:

  • あなたがいるので、0にあなたの変数を初期化する必要はありません。

    すべての変数のデフォルトの初期値はゼロ。

    docsを参照してください。

  • の基本文はアトミックに実行されるため、atomic {}文内に単一の命令を囲む必要はありません。可能であれば、検証プロセスの効率を上げるために、d_step {}を代わりに使用する必要があります。 Here関連するstackoverflow Q/Aトピックでを見つけることができます。 2次のいずれかの条件が成立するとき

  • init {}

    _pid == 0を持つことが保証されています

    • active proctypeは、ソースコード
    に登場する他のactive proctype前に宣言されている
  • init {}
  • を宣言されていません

    アクティブプロセス(init {}を含む)は、ソースコード内に出現順に生成されます。他のすべてのプロセスは、対応するrun ...ステートメントの出現順に生成されます。


私はあなたのモデルに以下の問題を特定した。

  • そのメモリ位置にのみ割り当てあなたがリリースpred[_pid] = tail

  • 後に読み込まれるので、指示pred[_pid] = 0は無用ですノードの後継ノードであるsucc_must_wait[_pid]0に設定すると、後継者が待機しているノードインスタンスを無効にしないでください。これはあなたがあなたの質問で特定したが解決できなかった問題です。これはatomic {}ブロックで囲む必要があり

    pid j; 
    for (j: 1..4) { 
        if 
         :: pred[j] == _pid -> pred[j] = 0; 
         :: else -> skip; 
        fi 
    } 
    

    :私が提案するソリューションは、次のコードを追加することです。

  • tail0に正しく設定すると、クリティカルセクションをちょう​​ど残したノードがキュー内のlastノードになっていることがわかります。また、この操作はatomic {}ブロックで正しく囲みます。しかし、- あなたがこれを入力しようとしているときにブロック他のプロセス- 何らかのアイドル状態でまだ待っていた -は、最初のアトミックブロックを実行することを決定し、現在の値をコピーしますtail- それはちょうど期限切れのノードに対応しています -自分自身にpred[_pid]メモリ位置に。クリティカルセクションを終了したばかりのノードが再び参加しようとした場合、succ_must_wait[_pid]の値を1に設定すると、別のインスタンスがプロセスの中で循環待ちになります。正しい方法は、この部分を後継者を解放するコードとマージすることです。

は、以下のインライン関数は、 放出所与のノードの後継に使用することができる

inline release_succ(i) 
{ 
    d_step { 
     pid j; 
     for (j: 1..4) { 
      if 
       :: pred[j] == i -> 
        pred[j] = 0; 
       :: else -> 
        skip; 
      fi 
     } 
     succ_must_wait[i] = 0; 
     if 
      :: tail == _pid -> tail = 0; 
      :: else -> skip; 
     fi 
    } 
} 

完全なモデル、以下:

byte ncritR; 
byte ncritW; 
byte tail; 
bool succ_must_wait[5]; 
byte pred[5]; 

init 
{ 
    skip 
} 

inline release_succ(i) 
{ 
    d_step { 
     pid j; 
     for (j: 1..4) { 
      if 
       :: pred[j] == i -> 
        pred[j] = 0; 
       :: else -> 
        skip; 
      fi 
     } 
     succ_must_wait[i] = 0; 
     if 
      :: tail == _pid -> tail = 0; 
      :: else -> skip; 
     fi 
    } 
} 

active [2] proctype reader() 
{ 
loop: 
    succ_must_wait[_pid] = 1; 
    d_step { 
     pred[_pid] = tail; 
     tail = _pid; 
    } 

trying: 
    (succ_must_wait[pred[_pid]] == 0) 

    ncritR++; 
    release_succ(_pid); 

    // critical section  
progress_reader: 
    assert(ncritR > 0); 
    assert(ncritW == 0); 

    ncritR--; 

    goto loop; 
} 

active [2] proctype writer() 
{ 
loop: 
    succ_must_wait[_pid] = 1; 

    d_step { 
     pred[_pid] = tail; 
     tail = _pid; 
    } 

trying: 
    (succ_must_wait[pred[_pid]] == 0) && (ncritR == 0) 

    ncritW++; 

    // critical section 
progress_writer: 
    assert(ncritR == 0); 
    assert(ncritW == 1); 

    ncritW--; 

    release_succ(_pid); 

    goto loop; 
} 

私はthを加えましたEモデルにプロパティを次

  • P0ライター4に等しい_pidは無限に多くの場合、その進行状態を通過するとは、それは無限に多くの場合、いくつかの命令を実行するチャンスを与えていることを提供する。

    ltl p0 { 
        ([]<> (_last == 4)) -> 
        ([]<> writer[4]@progress_writer) 
    }; 
    

    このプロパティはtrueである必要があります。

  • P1:クリティカルセクション内に複数のリーダー決してありません:明らか

    ltl p1 { 
        ([] (ncritR <= 1)) 
    }; 
    

    は、私たちは、このプロパティは、あなたの仕様に一致するモデルでfalseことを期待しています。

  • P2:このプロパティは、trueあるべき

    ltl p2 { 
        ([] (ncritW <= 1)) 
    }; 
    

    :クリティカルセクションに複数の作家が決してありません。

  • P3

    ltl p3 { 
        [] (
         (((pred[1] != 0) && (pred[2] != 0)) -> (pred[1] != pred[2])) && 
         (((pred[1] != 0) && (pred[3] != 0)) -> (pred[1] != pred[3])) && 
         (((pred[1] != 0) && (pred[4] != 0)) -> (pred[1] != pred[4])) && 
         (((pred[2] != 0) && (pred[3] != 0)) -> (pred[2] != pred[3])) && 
         (((pred[2] != 0) && (pred[4] != 0)) -> (pred[2] != pred[4])) && 
         (((pred[3] != 0) && (pred[4] != 0)) -> (pred[3] != pred[4])) 
        ) 
    }; 
    

    このプロパティはtrueなければならない。そのようなノードはノード0でない限り、同時に2つの他のノードの前身である任意のノードが存在しません。

  • P4

    ltl p4 { 
        [] (writer[4]@trying -> <> writer[4]@progress_writer) 
    }; 
    

    このプロパティtrueする必要があります:いつでも、それは最終的にそこに着くだろうクリティカルセクションにアクセスするための4試行に等しい_pid作家がいることを常に真であります。

検証の結果は我々の期待に一致します。

~$ spin -search -ltl p0 -a clhrw_lock.pml 
... 
Full statespace search for: 
    never claim    + (p0) 
    assertion violations + (if within scope of claim) 
    acceptance cycles  + (fairness disabled) 
    invalid end states  - (disabled by never claim) 

State-vector 68 byte, depth reached 3305, errors: 0 
... 

~$ spin -search -ltl p1 -a clhrw_lock.pml 
... 
Full statespace search for: 
    never claim    + (p1) 
    assertion violations + (if within scope of claim) 
    acceptance cycles  + (fairness disabled) 
    invalid end states  - (disabled by never claim) 

State-vector 68 byte, depth reached 1692, errors: 1 
... 

~$ spin -search -ltl p2 -a clhrw_lock.pml 
... 
Full statespace search for: 
    never claim    + (p2) 
    assertion violations + (if within scope of claim) 
    acceptance cycles  + (fairness disabled) 
    invalid end states  - (disabled by never claim) 

State-vector 68 byte, depth reached 3115, errors: 0 
... 

~$ spin -search -ltl p3 -a clhrw_lock.pml 
... 
Full statespace search for: 
    never claim    + (p3) 
    assertion violations + (if within scope of claim) 
    acceptance cycles  + (fairness disabled) 
    invalid end states  - (disabled by never claim) 

State-vector 68 byte, depth reached 3115, errors: 0 
... 

~$ spin -search -ltl p4 -a clhrw_lock.pml 
... 
Full statespace search for: 
    never claim    + (p4) 
    assertion violations + (if within scope of claim) 
    acceptance cycles  + (fairness disabled) 
    invalid end states  - (disabled by never claim) 

State-vector 68 byte, depth reached 3115, errors: 0 
... 
関連する問題