索引变量上支持通常的算术运算:
const N = 1
range T = 0..N
range R = 0..2*N
SUM = (in[a:T][b:T]->TOTAL[a+b]),
TOTAL[s:R] = (out[s]->SUM).
forall的用法:
||SWITCHES(N=3) =(forall[i:1..N] s[i]:SWITCH).
||SWITCHES(N=3) =(s[i:1..N]:SWITCH).
用这种方法去表示N个客户端加一个服务器的系统模型:
||N_CLIENT_SERVER(N=2)
= (forall[i:1..N] c[i]:CLIENT )||{c[i:1..N]}::(SERVER/{call/request , wait/reply})).
通过这个语法,我们就可以将我们可以将行人中的行为 wander 重新标记为行为 idle,如下所示:
TRAFFIC_LIGHT = (button -> YELLOW | idle -> GREEN),
GREEN = (green -> TRAFFIC_LIGHT),
YELLOW = (yellow -> RED),
RED = (red -> TRAFFIC_LIGHT).
PEDESTRIAN = ( button -> PEDESTRIAN
| wander -> PEDESTRIAN
).
||LIGHT_PEDESTRIAN = (TRAFFIC_LIGHT || PEDESTRIAN/{idle/wander}).
const N = 4
range T = 0..N
VAR = VAR[0],
VAR[u:T] = ( read[u]->VAR[u]
| write[v:T]->VAR[v]).
LOCK = (acquire->release->LOCK).
INCREMENT = (acquire->read[x:T]
-> (when (x<N) write[x+1]
->release->increment->INCREMENT
)
)+{read[T],write[T]}.
||COUNTER = (INCREMENT||LOCK||VAR)@{increment}.
信号量:
SEMAPHORE(N=0) = SEMA[N],
SEMA[v:Int] = (up->SEMA[v+1]
|when(v>0) down->SEMA[v-1]
).
信号量和Buffer:
const Max = 5
range Int = 0..Max
SEMAPHORE(I=0) = SEMA[I],
SEMA[v:Int] = (up->SEMA[v+1]
|when(v>0) down->SEMA[v-1]
).
BUFFER = (put -> BUFFER
|get -> BUFFER
).
PRODUCER = (empty.down->put ->full.up ->PRODUCER).
CONSUMER = (full.down ->get ->empty.up->CONSUMER).
||BOUNDEDBUFFER = (PRODUCER|| BUFFER || CONSUMER
||empty:SEMAPHORE(5)
||full:SEMAPHORE(0))@{put,get}.
如何使用FSP描述有界缓冲区呢:
BUFFER(N=5) = COUNT[0],
COUNT[i:0..N]
= ( when (i<N) put -> COUNT[i+1]
| when (i>0) get -> COUNT[i-1]
).
PRODUCER = (put -> PRODUCER).
CONSUMER = (get -> CONSUMER).
||BOUNDEDBUFFER = (PRODUCER || BUFFER(5) || CONSUMER).
wait-for cycle 是由于所有哲学家都在同一时间需要右边的刀叉造成的,要解决这个问题,一种方法就是让他们有不同的行为。比如,让奇数的哲学家先拿起他们的右叉,偶数的哲学家先拿起他们的左叉:
PHIL(I=0)
= ( when (I%2 == 0)
sitdown -> left.get -> right.get
-> eat -> left.put -> right.put
-> arise -> PHIL
| when (I%2 == 1)
sitdown -> right.get -> left.get
-> eat -> left.put -> right.put
-> arise -> PHIL
).
一个简单的资源分配器控制对固定数量的资源的访问(构造函数的参数定义了资源的数量)。线程可以通过参与get[n]的动作向控制器请求n个资源,他们必须在稍后的某个时间点通过调用put[n]来释放这些资源。对get[n]的调用应该暂停,直到有足够的资源可用。你可以假设在调用get时请求的资源数量总是大于0,但永远不会大于资源的总数N。
const N = 10 // number of resources
range T = 1..N // resources requested
const U = 5 // number of users
RESOURCE_USER =
( get[n:T] -> use[n] -> put[n] -> RESOURCE_USER ).
RESOURCE_ALLOC = RESOURCE_ALLOC[N],
RESOURCE_ALLOC[r:0..N] =
// note: index can be zero!
( when (r > 0) get[n:1..r] -> RESOURCE_ALLOC[r-n]
| put[n:T] -> RESOURCE_ALLOC[r+n]
).
在描述整个系统组成之前,我们需要指定一个安全属性与系统组成,以验证汽车在桥上不发生碰撞。下面列出了所需的属性。它规定,当红色汽车在桥上时,只有红色汽车可以进入,当蓝色汽车在桥上时,只有蓝色汽车可以进入。当桥是空的时候,红车或蓝车都可以进入。索引i用来计算目前在桥上的红(或蓝)车。
property ONEWAY =(red[ID].enter -> RED[1]
|blue[ID].enter -> BLUE[1]
),
RED[i:ID] = (red[ID].enter -> RED[i+1]
|when(i==1)red[ID].exit -> ONEWAY
|when(i>1) red[ID].exit -> RED[i-1]
),
BLUE[i:ID]= (blue[ID].enter -> BLUE[i+1]
|when(i==1)blue[ID].exit -> ONEWAY
|when(i>1) blue[ID].exit -> BLUE[i-1]
).
抛硬币的有活性要求现在可以表示为:
progress HEADS = {heads}
progress TAILS = {tails}
progress HEADSorTAILS = {heads,tails}
Action Priority
桥梁模型现在可以计算在两端等待的汽车数量。当汽车请求进入时,这个计数被递增,当汽车进入桥时被递减。我们对修改后的BRIDGE过程的第一次尝试,使用了这种对等待车辆的计数,具体如下。只有当桥上没有蓝色汽车,也没有蓝色汽车在等待时,红色汽车才被允许进入桥上。只有当桥上没有红色汽车,也没有红色汽车在等待进入桥上时,才允许蓝色汽车进入桥上。修订后的BRIDGE流程如下:
/* nr - number of red cars on the bridge
nb - number of blue cars on the bridge
wr - number of red cars waiting to enter
wb - number of blue cars waiting to enter
*/
BRIDGE = BRIDGE[0][0][0][0],
BRIDGE[nr:T][nb:T][wr:T][wb:T] =
(red[ID].request -> BRIDGE[nr][nb][wr+1][wb]
|when (nb==0 && wb==0)
red[ID].enter -> BRIDGE[nr+1][nb][wr-1][wb]
|red[ID].exit -> BRIDGE[nr-1][nb][wr][wb]
|blue[ID].request -> BRIDGE[nr][nb][wr][wb+1]
|when (nr==0 && wr==0)
blue[ID].enter -> BRIDGE[nr][nb+1][wr][wb-1]
|blue[ID].exit -> BRIDGE[nr][nb-1][wr][wb]
).
Readers–Writers Problem
set Actions = {acquireRead,releaseRead,
acquireWrite,releaseWrite}
READER =
(acquireRead->examine->releaseRead->READER)
+Actions
\ {examine}.
WRITER =
(acquireWrite->modify->releaseWrite->WRITER)
+Actions
\ {modify}.
const False = 0 const True = 1
range Bool = False..True
const Nread = 2 // Maximum readers
const Nwrite= 2 // Maximum writers
RW_LOCK = RW[0][False],
RW[readers:0..Nread][writing:Bool] =
(when (!writing)
acquireRead ->RW[readers+1][writing]
|releaseRead ->RW[readers-1][writing]
|when (readers==0 && !writing)
acquireWrite->RW[readers][True]
|releaseWrite ->RW[readers][False]
).
property SAFE_RW
= (acquireRead->READING[1]
|acquireWrite->WRITING
),
READING[i:1..Nread]
= (acquireRead->READING[i+1]
|when(i>1) releaseRead ->READING[i-1]
|when(i==1)releaseRead ->SAFE_RW
),
WRITING = (releaseWrite->SAFE_RW).
||READWRITELOCK = (RW_LOCK || SAFE_RW).
progress WRITE = {writer[1..Nwrite].acquireWrite}
progress READ = {reader[1..Nread].acquireRead}
||RW_PROGRESS = READERS_WRITERS
>>{reader[1..Nread].releaseRead,
writer[1..Nread].releaseWrite}.
FSP—流动性
fluent GREEN[i:1..2]
= <{green[i]}, {yellow[i],red[i]}> initially 1
时间逻辑—“always”
assert ALWAYS_A_COLOUR = [](GREEN || YELLOW || RED)
时间逻辑—“eventually”
assert EVENTUALLY_RED = <>RED
时间逻辑—“next”操作符
assert BUTTON_TO_YELLOW = (button -> X YELLOW)
assert MUST_EXIT
= forall[i:1..N]
[](p[i].enter -> <>p[i].exit)
下面的断言说,进程a和b最终都将获得该资源:
assert EVENTUALLY_ACQUIRE
= (<>a.acquire && <>b.acquire)
下面的断言说,获取资源的进程最终将释放资源的情况永远是这样的:
assert USE_BEFORE_RELEASE
= []((a.acquire -> <>a.release) && (b.acquire -> <>b.release))
下面的断言说,每当一个进程获得资源时,它会一直持有它,直到另一个进程获得它:
fluent A_HOLDS
= <a.acquire, a.release>
fluent B_HOLDS
= <b.acquire, b.release>
assert HOLDS_UNTIL
= []((A_HOLDS U B_HOLDS) && (B_HOLDS U A_HOLDS))
HMAS Emafor是一艘航空母舰,飞机从它出发执行任务,然后再次降落。为了安全,任何时候跑道上只能有一架飞机。为了确保这一点,飞机加入到达队列(enterArrQ)或离开队列(enterDepQ),等待降落或起飞的机会。两个队列的最大长度都是两个平面。飞机只有在得到空中交通管制员(ATC)的允许后才能起飞或降落。下面的FSP模型描述了这个系统:
const MaxID = 2
range ID = 0..MaxID - 1
ARRIVE = ARRIVE[0],
ARRIVE[n:ID] = (enterArrQ[n] -> ARRIVE[n+1 % MaxID]).
ARRIVAL_Q = (enterArrQ[n: ID] -> ARRIVAL_Q[n]),
//one plane in queue
ARRIVAL_Q[n0: ID] =
(enterArrQ[n: ID] -> ARRIVAL_Q[n0][n]
| permitLand[n0] ->land[n0] -> ARRIVAL_Q
),
//two planes in queue
ARRIVAL_Q[n0: ID][n1: ID] =
(permitLand[n0] -> land[n0] -> ARRIVAL_Q[n1]).
DEPART = DEPART[0],
DEPART[n: ID] = (enterDepQ[n] -> DEPART[(n+1) % MaxID]).
DEPARTURE_Q = (enterDepQ[n: ID] -> DEPARTURE_Q[n]),
//one plane in queue
DEPARTURE_Q[n0: ID] =
(enterDepQ[n: ID] -> DEPARTURE_Q[n0][n]
| permitTakeoff[n0] -> takeoff[n0] -> DEPARTURE_Q
),
//two planes in queue
DEOARTURE_Q[n0: ID][n1: ID] =
(permitTakeoff[n0] -> takeoff[n0] -> DEPARTURE_Q[n1]).
ACT =
(permitLand[n: ID] -> land[n] -> ACT
| permitTakeoff[n: ID] -> takeoff[n] -> ACT
).
|| CARRIER = (ARRIVE || ARRIVAL_Q || DEPART || DEPARTURE_Q || ACT) >>{permitLand[ID]}.
property ARRIVAL_MAX = MAX[MaxID-1],
MAX[m:ID] =
(when (m < 1) enterArrQ[n:ID] -> MAX[m+1]
|when (m > 0) takeoff[n:ID] -> MAX[m-1]
).
||SAFE_CARRIER = (CARRIER||ARRIVAL_MAX).
assert TAKEOFF = <>takeoff
墨尔本一家商店有一台卫生纸自动售货机,一次最多可以装5卷卫生纸。三名顾客(C=3)从自动售货机购买卫生纸,每次一卷,除非自动售货机是空的,否则他们会等待重新装满。供应商定期给机器加满油,但只有当机器是空的时候才加。这台机器最初装满了R卷卫生纸。墨尔本会定期封锁,下一个使用机器的顾客会感到恐慌(这是一种行动!),然后买下所有剩下的卫生纸卷。在锁定结束之前,供应商无法重新填充机器,所有其他客户必须等待。写下FSP流程CUSTOMER, SUPPLIER, MACHINE和locked,以及一个复合流程系统来建模这个系统。
const R = 5
const C = 3
const True = 0
const False = 1
range T = 0..R
range Bool = True..False
MACHINE = MACHINE[5],
MACHINE[i:T] =
(when (i>0) opened -> buy[1] -> MACHINE[i-1]
|when (i==0) opened -> fill -> MACHINE[5]
|when (i>0) locked -> buy[i] -> MACHINE[0]
).
SUPPLIER = (opened -> fill -> SUPPLIER).
CUSTOMER =
(opened -> buy[1] -> CUSTOMER
|locked -> panice -> buy[n:T]-> CUSTOMER
).
LOCKED = LOCKED[False],
LOCKED[m:Bool] =
(when (m == False) locked -> LOCKED[True]
|when (m == True) opened -> LOCKED[False]
).
||SYSTEM = (LOCKED||SUPPLIER||c[i:1..C]:CUSTOMER||{c[i:1..C]}::MACHINE)
/{locked/{[c:1..C].locked} , opened/{[c:1..C].opened}}.
以下是FSP对水位传感器的描述:
/*
* FSP code for a sensor that monitors a tank volume and checks
* whether the level is low, high, or ok.
*/
// The readings of the level are assumed to be provided by an
// external process; the action reading[v] means a reading of
// ‘v’ is received. There are 7 different levels:
range T = 0..6
SENSOR
= (read[v:T] -> MEASURE[v]),
MEASURE[v:T]
= ( when (v < 2) low -> SENSOR
| when (v > 4) high -> SENSOR
| when (v > 1 && v < 5) ok -> SENSOR
).
The drinks dispensing machine:
/*
* FSP code for a drinks dispensing machine.
* Assumption: Once there is 15 cents to buy the drink,
* the user cannot add more coins.
*/
DRINKS
= CREDIT[0],
// Inserting money is modelled using an indexed process,
// where the index represents the amount entered so far
CREDIT[i:0..30]
= ( when (i <= 10) insert[5] -> CREDIT[i+5]
| when (i <= 10) insert[10] -> CREDIT[i+10]
| when (i <= 10) insert[20] -> CREDIT[i+20]
// When credit is sufficient, give i-15 in change
// (since the cost is 15 cents):
| when (i >= 15) press_button -> CHANGE[i-15]
),
// 20c change is not possible: the maximal input is 10+20,
// so the maximal change is 15. The index represents the
// amount of change owing:
CHANGE[i:0..15]
= ( when (i > 10) return[10] -> CHANGE[i-10]
| when (i >= 5 && i < 20) return[5] -> CHANGE[i-5]
| when (i < 5) dummy -> END
)\{dummy}.
Moving around on the 4 × 4 board:
/*
* FSP code for a pebble moving around on an NxN board.
*/
const N = 4
range T = 1..N
P = STATE[1][1], // The pebble starts in row 1, column 1
STATE[row:T][col:T]
= ( when (row > 1) down -> STATE[row-1][col]
| when (row < N) up -> STATE[row+1][col]
| when (col > 1) left -> STATE[row][col-1]
| when (col < N) right -> STATE[row][col+1]
).
RESOURCE SHAREv3:
RESOURCE = (acquire -> release -> RESOURCE).
USER = (acquire -> use -> release -> USER).
||RESOURCE_SHAREv3(N=5) = (u[1..N]:USER || {u[1..N]}::RESOURCE).
Here are the five dining philosophers, assisted by a butler:
const PHIL_NUMBER = 5
PHIL = (sitdown -> right.get -> left.get -> eat
-> left.put -> right.put -> arise -> PHIL).
FORK = (get -> put -> FORK).
BUTLER = BUTLER[0],
BUTLER[i:0..PHIL_NUMBER-1]
= ( when (i < PHIL_NUMBER-1) sitdown -> BUTLER[i+1]
| when (i > 0) arise -> BUTLER[i-1]
).
||DINERS(N=PHIL_NUMBER)
= forall [i:0..N-1]
( phil[i]:PHIL
|| {phil[i].left,phil[((i-1)+N)%N].right}::FORK
).
||DINERS_WITH_BUTLER
= (DINERS || {phil[i:0..PHIL_NUMBER-1]}::BUTLER).
const N = 10
range T = 0..N
// The safety property that ensures the number of
// passengers in the lift never exceeds 10
// (or, incidentally, falls below 0!)
property FULL = FULL[0],
FULL[i:T] = (when (i<N) enter -> FULL[i+1]
|when (i>0) exit -> FULL[i-1]
).
// The following implementation of a lift (v1) will cause a property
// violation when composed with the safety property.
LIFTv1 = LIFTv1[0],
LIFTv1[i:T] =
( enter -> LIFTv1[i+1]
| exit -> LIFTv1[i-1]
).
// The following implementation of a lift (v2) uses guards to prevent
// the safety property being violated, and hence will not cause a
// property violation when composed with the safety property.
LIFTv2 = LIFTv1[0],
LIFTv1[i:T] =
( when (i<N) enter -> LIFTv1[i+1]
| when (i>0) exit -> LIFTv1[i-1]
).
||SAFE_LIFTv1 = (LIFTv1 || FULL).
||SAFE_LIFTv2 = (LIFTv2 || FULL).
const False = 0
const True = 1
range Bool = False..True
const Nuser = 2
const Nsystem = 2
// As with the reader-writer model, we need to add the full set
// of acquire/release actions to the alphabets of both user and
// system processes
set Actions = {systemAcquire, systemRelease,
userAcquire, userRelease}
// The console acts as a binary semaphore that only allows one
// process access at a time
CONSOLE(N=1) = CONSOLE[0],
CONSOLE[v:0..N] =
( when (v<1) systemAcquire -> CONSOLE[v+1]
| when (v>0) systemRelease -> CONSOLE[v-1]
| when (v<1) userAcquire -> CONSOLE[v+1]
| when (v>0) userRelease -> CONSOLE[v-1]
).
USER = (userAcquire -> userConsole -> userRelease -> USER)
+ Actions.
SYSTEM = (systemAcquire -> systemConsole -> systemRelease -> SYSTEM)
+ Actions.
||OS = (user[1..Nuser]:USER
||system[1..Nsystem]:SYSTEM
||{user[1..Nuser],
system[1..Nsystem]}::CONSOLE(1)).
// Under fair scheduling assumptions the OS process will provide
// equal access to both user and system processes. However, user
// processes may be denied access to the console if system processes
// have a higher priority when attempting to acquire access,
// as denoted by the action priority operator below.
||OS_PROGRESS = OS <<{system[1..Nsystem].systemAcquire}.
progress USER_PROG = {user[1..Nuser].userAcquire}
progress SYS_PROG = {system[1..Nsystem].systemAcquire}
const True = 1
const False = 0
range Bool = False..True
set BoolActions = {setTrue, setFalse, check[False], check[True]}
BOOLVAR = VAL[False],
VAL[v:Bool] = ( setTrue -> VAL[True]
| setFalse -> VAL[False]
| check[v] -> VAL[v]
).
||FLAGS = (flag1:BOOLVAR || flag2:BOOLVAR).
FIELD = (enter -> exit -> FIELD).
N1 = (flag1.setTrue -> flag2.check[t:Bool] ->
(when (t == True) flag1.setFalse -> N1
|when (t == False) n1.enter -> n1.pickBerries -> n1.exit ->
flag1.setFalse -> N1)
).
N2 = (flag2.setTrue -> flag1.check[t:Bool] ->
(when (t == True) flag2.setFalse -> N2
|when (t == False) n2.enter -> n2.pickBerries -> n2.exit ->
flag2.setFalse -> N2)
).
||WAR = (FLAGS || N1 || N2 || {n1,n2}::FIELD).
// Safety property to ensure mutual exclusion (one neighbour leaves the
// field before the other enters).
property MUTEX = ( n1.enter -> n1.exit -> MUTEX
| n2.enter -> n2.exit -> MUTEX).
||CHECK = (WAR || MUTEX).
// Progress property to ensure that both neighbours are able to pick berries
progress PICK = {{n1,n2}.pickBerries}
// A version of the warring neighbours model with greedy neighbours who refuse
// to lower their flags when trying to access the field.
||WAR_PROGRESS = WAR >>{{flag1,flag2}.setFalse}.