// abstype DB = ptr // absview DBread_v absview DBwrite_v // fun DBread (pf: !DBread_v | db: DB): void fun DBwrite (pf: !DBwrite_v | db: DB): void //[DBread] を呼び出してデータベースから読み出すためには、(抽象) view [DBread_v] の証明が必要です。 [DBwrite] を呼び出してデータベースに書き込むためには、(抽象) view [DBwrite_v] の証明が必要です。
// absvtype DBshell (r: int, w: int) = ptr // vtypedef DBshell = [r,w:int] DBshell (r, w) //整数 r と w が与えられたとき型 [DBshell(r, w)] の値は、現時点で r 個の読み出しアクセスと w 個の書き込みアクセスを許可しているデータベースを内包した、状態付きデータベース (database-with-state) 見なせます。 次の2つの証明関数は、パラメータ r と w に対するいくつかの強制を示しています:
// praxi lemma_DBshell_param {r,w:int} (!DBshell (r, w)): [0 <= r; 0 <= w; w <= 1] void praxi lemma_DBshell_param2 {r,w:int} (!DBshell (r, w)): [r == 0 || (r > 0 && w == 0)] void //型 [DBshell(r, w)] が与えられたとき、証明関数 [lemma_DBshell_param] は r は自然数で w は 0 か 1 であることを示しています; 証明関数 [lemma_DBshell_param2] は、もし r が正の数であれば w は 0 でなければならないことを示しています。
状態付きデータベースに関するいくつかの関数を次に列挙します:
// fun DBshell_dbget (x: !DBshell): DB // fun DBshell_nread {r,w:int} (x: !DBshell (r, w)): int (r) fun DBshell_nwrite {r,w:int} (x: !DBshell (r, w)): int (w) //[DBshell_dbget] が、与えられた状態付きデータベースに関連したデータベースを返す意図を持っていることは明確です。 [DBshell_nread] と [DBshell_nwrite] に関しては、それぞれ r と w の値を獲得するために、型 [DBshell(r, w)] の状態付きデータベースに対して呼び出すことができます。
// fun DBshell_acquire_read {r:int} (x: !DBshell (r, 0) >> DBshell (r+1, 0)): (DBread_v | void) fun DBshell_release_read {r,w:int} (pf: DBread_v | x: !DBshell (r, w) >> DBshell (r-1, w)): void // fun DBshell_acquire_write (x: !DBshell (0, 0) >> DBshell (0, 1)): (DBwrite_v | void) fun DBshell_release_write {r,w:int} (pf: DBwrite_v | x: !DBshell (r, w) >> DBshell (r, w-1)): void //データベースから読み出すためには view [DBread_v] の証明が必要です。 それを得るためには、現時点で書き込みアクセスを許可していない状態付きデータベースに対して [DBshell_acquire_read] を呼び出す必要があります。 同様に、データベースへ書き込むためには view [DBwrite_v] の証明が必要です。 それを得るためには、現時点で読み出しアクセスも書き込みアクセスも許可していない状態付きデータベースに対して [DBshell_acquire_write] を呼び出す必要があります。
abstype SDBshell = ptr
予想できることですが、共有された状態付きデータベース内の状態付きデータベースを保護するために mutex を使います。
次の2つの関数は、保護された状態付きデータベースを獲得/解放をするために呼び出されます:
fun SDBshell_acquire (sx: SDBshell): DBshell fun SDBshell_release (sx: SDBshell, x: DBshell): voidここで次に宣言された関数の実装を見てみましょう:
// fun SDBshell_acquire_read (sx: SDBshell): (DBread_v | void) fun SDBshell_release_read (pf: DBread_v | sx: SDBshell): void // fun SDBshell_acquire_write (sx: SDBshell): (DBwrite_v | void) fun SDBshell_release_write (pf: DBwrite_v | sx: SDBshell): void //これらは並行プログラミングにおいて (競合状態を引き起こさずに) データベースアクセスを安全にサポートするために呼び出されます。
共有された状態付きデータベース内には条件変数もあります。 もしプロセスが読み出そうとした時にそのデータベースが別のプロセスに書き込みアクセスを許可していたなら、次の関数 [SDBshell_wait_read] がその条件変数を待ち合わせるプロセスによって呼び出されます:
// extern fun SDBshell_wait_read {r:int} ( sx: SDBshell, x: !DBshell (r, 1) >> DBshell ) : void // end of [SDBshell_wait_read] //同様に、もしプロセスが書き込もうとした時にそのデータベースが別のプロセスに読み出しアクセスか書き込みアクセスを許可していたなら、次の関数 [SDBshell_wait_write] がその条件変数を待ち合わせるプロセスによって呼び出されます:
extern fun SDBshell_wait_write {r,w:int | r+w >= 1} ( sx: SDBshell, x: !DBshell (r, w) >> DBshell ) : void // end of [SDBshell_wait_write]条件変数を待ち合わせているプロセスを起こすために、次の関数 [SDBshell_signal] を使うことができます:
// extern fun SDBshell_signal (sx: SDBshell): void //さらに、[SDBshell_acquire_read] と [SDBshell_acquire_write] の実装を容易にするために、次のように2つの補助関数を宣言します:
extern fun SDBshell_acquire_read2 (sx: SDBshell, x: !DBshell >> _): (DBread_v | void) extern fun SDBshell_acquire_write2 (sx: SDBshell, x: !DBshell >> _): (DBwrite_v | void)次のコードは [SDBshell_acquire_read] を実装しています:
implement SDBshell_acquire_read (sx) = (pf | ()) where { val x = SDBshell_acquire (sx) val (pf | ()) = SDBshell_acquire_read2 (sx, x) val () = SDBshell_release (sx, x) } implement SDBshell_acquire_read2 (sx, x) = let // prval () = lemma_DBshell_param (x) // val w = DBshell_nwrite (x) // in // if w = 0 then DBshell_acquire_read (x) else let val () = SDBshell_wait_read (sx, x) in SDBshell_acquire_read2 (sx, x) end // end of [else] // end // end of [SDBshell_acquire_read2]次のコードは [SDBshell_release_read] を実装しています:
implement
SDBshell_release_read
(pf | sx) = () where
{
val x = SDBshell_acquire (sx)
val () = DBshell_release_read (pf | x)
val r = DBshell_nread (x)
val () =
if r = 0
then SDBshell_signal (sx)
// end of [if]
val () = SDBshell_release (sx, x)
}
次のコードは [SDBshell_acquire_write] を実装しています:
implement SDBshell_acquire_write (sx) = (pf | ()) where { val x = SDBshell_acquire (sx) val (pf | ()) = SDBshell_acquire_write2 (sx, x) val () = SDBshell_release (sx, x) } implement SDBshell_acquire_write2 (sx, x) = let // prval () = lemma_DBshell_param (x) prval () = lemma_DBshell_param2 (x) // val r = DBshell_nread (x) // in // if r = 0 then let val w = DBshell_nwrite (x) in if w = 0 then DBshell_acquire_write (x) else let val () = SDBshell_wait_write (sx, x) in SDBshell_acquire_write2 (sx, x) end // end of [else] // end of [if] end // end of [then] else let val () = SDBshell_wait_write (sx, x) in SDBshell_acquire_write2 (sx, x) end // end of [else] // end // end of [SDBshell_acquire_write2]次のコードは [SDBshell_release_write] を実装しています:
implement
SDBshell_release_write
(pf | sx) = () where
{
val x = SDBshell_acquire (sx)
val () = DBshell_release_write (pf | x)
val () = SDBshell_signal (sx)
val () = SDBshell_release (sx, x)
}
DB_read_write.sats DB_read_write.dats