Send & Rev
稍微复杂点的程序中都会涉及数据的发送与接收,本例子基于发送器Sender与接收器Receiver来实现数据的变更。假设Sender发送一个数据给Receiver,Receiver接收后将确认信息发送给Sender,增加两个状态rdy,ack来表示发送者状态和确认状态,当rdy等于ack时允许sender发送数据,当rdy与ack不一致时则意味着Receiver有数据需要接收,用val来缓存sender发送的数据,Receiver用data来存储缓存的val数据。
创建Module,引入Naturals,定义变量
----------------------------- MODULE Interface ------------------------------
EXTENDS Naturals
CONSTANT Data
VARIABLE rdy,ack,data,val
\* 限制上述变量的范围
TypeInvariant == rdy \in {0,1} /\ ack \in {0,1} /\ data \in Data /\ val \in Data
=============================================================================
初始状态
Init == TypeInvariant /\ rdy = ack /\ data = val
发送与接收条件
CanSend == rdy = ack
CanAck == rdy # ack
发送数据
\* 用UNCHANGED ack替代 ack'=ack,用UNCHANGED data表示 data'=data
\* Send 数据时修改rdy的状态,保证rdy与ack是不一致的以便Ack来接收数据
Send(d) == CanSend /\ val' = d /\ rdy' = 1 - rdy /\ UNCHANGED ack /\ UNCHANGED data
接收数据
\* 接收数据时修改ack与rdy保持一致,以便后续进行send
Ack == CanAck /\ data' = val /\ ack' = 1 - ack /\ UNCHANGED rdy /\ UNCHANGED val
下一步动作
\* 这里 Send与Ack是或的关系,根据发送与接收条件,Send与Ack只有一个动作能执行
\* \E d \in Data:Send(d) 表示Send任意存在于Data中的d
Next == (\E d \in Data:Send(d)) \/ Ack
在程序调试或者执行的过程中,通常需要打印出中间状态来查看代码是否正确。在TLA+中,可以通过引入TLC Module来实现打印功能。
EXTENDS TLC
Print(out,val)
\* Print 会同时将out,val的值打印出来,同时返回val的值。当用在表达式中时,val的值通常为TRUE
可以通过自定义打印动作来打印出更多的参数,例如
\* 下面PrintVal可以打印id,exp,TRUE三个参数,同时返回TRUE。
PrintVal(id, exp) == Print(<<id, exp>>, TRUE)
\* 添加更多打印参数,直接添加即可,如
PrintVal(id,exp,res) == Print(<<id,exp,res>>,TRUE)
Interface完整实现
EXTENDS Naturals,TLC
CONSTANT Data
VARIABLE rdy,ack,data,val
PrintVal(id, exp) == Print(<<id, exp>>, TRUE)
TypeInvariant == rdy \in {0,1} /\ ack \in {0,1} /\ data \in Data /\ val \in Data
CanSend == rdy = ack
CanAck == rdy # ack
Init == TypeInvariant /\ rdy = ack /\ data = val
Send(d) == CanSend /\ val' = d /\ rdy' = 1 - rdy /\ UNCHANGED ack /\ UNCHANGED data /\ PrintVal("Send",d)
Ack == CanAck /\ data' = val /\ ack' = 1 - ack /\ UNCHANGED rdy /\ UNCHANGED val /\ PrintVal("Ack",val)
Next == (\E d \in Data:Send(d)) \/ Ack
输出
设置Init,Next以及Data的配置,设置Data输入集为{1,2,3,4}如下:
由于上述TLA+代码中添加量Print打印,可以在执行后的User Output中查看输出结果: