-------------------------------------------------------------------------------
-- leader.m
-- leader selection protocol
-------------------------------------------------------------------------------
-- To verify ae_leader:
-- use LIVENESS "ae_leader"
-- To verify one_leader
-- use ASSERT(!leader) before setting leader
-------------------------------------------------------------------------------
CONST
NumNodes : 7;
BufferSize : 2 * NumNodes;
TYPE
NodeID : 0..NumNodes-1;
BufferLength : 0..BufferSize;
BufferIndex : 0..BufferSize-1;
MsgStage : ENUM { first, second };
Message : RECORD
stage : MsgStage;
ID : NodeID;
END;
Channel : RECORD
length : BufferLength;
buffer : ARRAY [BufferIndex] OF Message;
END;
NodeState : ENUM {
start,
active,
inactive,
inactiveget,
getfirst,
getsecond
};
Node : RECORD
state : NodeState;
maxID : NodeID;
nbrID : NodeID;
NID : NodeID;
msgstage : MsgStage;
END;
VAR
channels : ARRAY [NodeID] OF Channel;
nodes : ARRAY [NodeID] OF Node;
leader : boolean;
-------------------------------------------------------------------------------
-- buffered channel
-------------------------------------------------------------------------------
FUNCTION isfull(Chan: Channel) : boolean;
BEGIN
RETURN Chan.length = BufferSize;
END;
FUNCTION isempty(Chan: Channel) : boolean;
BEGIN
RETURN Chan.length = 0;
END;
PROCEDURE input(VAR Chan: Channel; VAR stage: MsgStage; VAR ID: NodeID);
VAR
i : BufferLength;
BEGIN
assert(!isempty(Chan));
stage := Chan.buffer[0].stage;
ID := Chan.buffer[0].ID;
Chan.length := Chan.length-1;
-- shift buffer
FOR i:= 0 TO Chan.length DO
Chan.buffer[i] := Chan.buffer[i+1]
END;
CLEAR Chan.buffer[Chan.length];
END;
PROCEDURE output(VAR Chan: Channel; stage: MsgStage; ID: NodeID);
BEGIN
assert(!isfull(Chan));
Chan.buffer[Chan.length].stage := stage;
Chan.buffer[Chan.length].ID := ID;
Chan.length := Chan.length+1;
END;
-------------------------------------------------------------------------------
-- node
-------------------------------------------------------------------------------
RULESET n: NodeID DO
ALIAS
node: nodes[n];
left: channels[n];
right: channels[(n+1)%NumNodes]
DO
RULE "send first"
node.state = start & !isfull(right)
==>
BEGIN
output(right, first, node.maxID);
node.state := active;
END;
RULE "active"
node.state = active & !isempty(left)
==>
VAR
stage : MsgStage;
BEGIN
input(left, stage, node.NID);
IF stage = first THEN
node.state := getfirst;
node.nbrID := node.NID
ELSIF stage = second THEN
node.state := getsecond;
ELSE
ERROR "wrong message type";
END;
END;
RULE "getfirst"
node.state = getfirst & !isfull(right)
==>
BEGIN
IF node.nbrID != node.maxID THEN
output(right, second, node.nbrID);
node.state := active;
ELSIF node.maxID = NumNodes-1 THEN
ASSERT(!leader);
leader := true;
node.state := active;
ELSE
ERROR "protocol fails"
END
END;
RULE "getsecond"
node.state = getsecond & !isfull(right)
==>
BEGIN
IF node.nbrID > node.NID & node.nbrID > node.maxID THEN
output(right, first, node.nbrID);
node.state := active;
node.maxID := node.nbrID;
ELSE
CLEAR node;
node.state := inactive;
END;
END;
RULE "inactive"
node.state = inactive & !isempty(left)
==>
BEGIN
input(left, node.msgstage, node.NID);
node.state := inactiveget;
END;
RULE "inactive get"
node.state = inactiveget & !isfull(right)
==>
BEGIN
output(right, node.msgstage, node.NID);
CLEAR node;
node.state := inactive;
END;
ENDALIAS;
ENDRULESET;
-------------------------------------------------------------------------------
LIVENESS "ae_leader"
ALWAYS EVENTUALLY leader;
-------------------------------------------------------------------------------
STARTSTATE
CLEAR channels;
CLEAR nodes;
FOR i: NodeID DO
nodes[i].maxID := NumNodes-1 - i;
END;
leader := false;
END;