时序属性
Termination
最简单的时序属性是Termination,Termination是算法终止的要求,如果算法崩溃或者死锁或者进入一个无限循环,那么久违反了Termination规范。
假设有一辆车在过交通灯时,有红灯和绿灯两种选择,绿灯行,红灯停,红绿灯交替亮起。
NextColor(c) == CASE c = "red" -> "green"
[] c = "green" -> "red"
(*--algorithm traffic
variables
at_light = TRUE,
light = "red";
process light = "light"
begin
Cycle:
while at_light do
light := NextColor(light);
end while;
end process;
process car = "car"
begin
Drive:
when light = "green";
at_light := FALSE;
end process;
end algorithm;*)
上述代码在TLC中运行时,如果在Model Overview > What to Check? > Properties, 不选中Termination,则不会报错,选中Termination,运行时会报错Temporal properties were violated.查看Error-trace,发现有个Stuffering。
Stuffering && liveness
TLA+是动作的时序逻辑,Model的每一步检查都代表着时间上的一个动作,TLC的工作原理是查看每一步中所有启用的标签,并选择一个执行,然而TLC还有另外一种选择,即不执行任何动作,这种情况称之为stuffering。大多数情况下,stuffering不会改变规范,既然没有动作发生,那就什么都不会改变。但是如果一直stuffering,就好像是动作执行被阻塞的一样,状态不会改变。
所有的不变量的检查都是安全的,用来检查不会到达一个无效状态。由于有效状态下的stuffering还是一种有效状态,因此TLC通常不会尝试stuffering。然而在大多数时序属性用来进行活性检查,活性检查(liveness check)是验证系统最终能够按照期望运行。上述例子中,由于stuffering,规范运行将不会终止。
stuffering是有用的,它可以表示服务器崩溃或者进程超时,或者信号量永远等不到。如果要明确的排除stuffering,就需要增加fairness。
Fairness
fairness有两种:弱(weak),强(strong )。只要声明了fair的动作保持启用,就能达到弱公平性(weakly fair)。为上述例子添加fairness:
fair process light = "light" \* 通过fair关键字添加fairness
begin
Cycle:
while at_light do
light := NextColor(light);
end while;
end process;
fair process car = "car"
begin
Drive:
when light = "green";
at_light := FALSE;
end process;
如果上述例子中只给进程car添加fair,运行Model check,仍然stuffering,light不会循环变更;如果只给进程light添加fair,运行Model check,发现car永远不会Drive。如果两个进程都添加fair,运行后还是会出错。出现问题的原因是如果light在green和red之间循环,那car进程的Drive也将不断在禁用和启用之间变更。因此为了保证Car能够完成Drive,需要light保持green。
这时候强公平性就起作用了,如果一个强公平性的动作反复启动和禁用,那该动作一定会发生。可以通过fair+ 来将进程变成强公平性。如:
fair+ process car = "car"
begin
Drive:
when light = "green";
at_light := FALSE;
end process;
重新执行model check,这次将按照预期进行,不会出现错误。需要注意的是,在Car为fair+的情况下,仍然需要light为fair。
上述fairness的强弱都是针对进程的,fairness的强弱也可以针对算法或者标签。
--fair algorithm \* 针对算法
A:+ \* 针对标签A,如果A本来是unfair,添加+后,A将变为weakly fair
\* 如果A本来是wf,则添加+后,A将变为string fair
时序操作符
首先定义P和Q为布尔语句
[]
[]等价于always。[]P意味着对于动作中的所有状态,P都是TRUE。
<>
<>等价于eventually,<>P意味着对于动作中的所有状态,至少存在一个状态是的P为TRUE或者最终态P为TRUE。
Termination == <>(\A self \in ProcSet: pc[self] = "Done")
\* 可以理解为最终所有的process状态都是Done
~>
~>等价于 leads-to。 P ~>Q 意思是如果在某个状态P为true,那么Q也为true或者Q在未来的某个状态为true。leads-to是不可逆的,一旦发生,即使P之后是false,那Q也必须出现为true的情况。
L == (light = "green") ~> ~at_light
\* 当light为green时,一定会导致at_light变更。前后具有因果关系。
[]<> and <>[]
[]<>P意思是P总是最后为true; <>[]P意思是P最后总是为true。对于有限的规范来说,这两个是相同的意思,P在终止是为true。但对于无限的规范来说,<>[]P意思是在某个状态上P为true,并永远保持true。而[]<>P意思是如果P变为false,P最终会再次变成true。
THE END!