This interesting example describes a protocol used in systems that share a unique resource, like
for instance a token that gives privileges to it's owner. Working on a determined connectivity
structure, a minimum spanning tree, the nodes in the system evolve by requesting, waiting, getting
and releasing the object. The connectivity structure evolves along with the nodes in such a way that
the links give the direction to either the object or the nodes that are waiting for it. Since there can
be several nodes requesting the object in parallel a waiting queue can be established, distributed
throughout the system.
First off the nodes behavior is defined: A TerminalOwner that waits for requests for the object,
being that no request has arrived to it; an Owner that either accepts requests for the object or
releases it to the head of the waiting queue; An Idle that passes along requests for the object or
makes it's own request for it; A TerminalWaiter that either passes along requests for the object
or receives it and a Waiter that has the same choice, differing only on the necessary update of
the connectivity links.
defproc
TerminalOwner(find,move,obj) =
find?(mymove,myfind).Owner(find,move,myfind,mymove,obj)
and
Owner(find,move,link,queue,obj) =
select {
find?(mymove,myfind).(Owner(find,move,myfind,queue,obj) |
link!(mymove,find));
tau.(Idle(find,move,link) | queue!(obj))
}
and
Idle(find,move,link) =
select {
find?(mymove,myfind).(Idle(find,move,myfind) |
link!(mymove,find));
tau.(TerminalWaiter(find,move) | link!(move,find))
}
and
TerminalWaiter(find,move) =
select {
find?(mymove,myfind).Waiter(find,move,myfind,mymove);
move?(obj).TerminalOwner(find,move,obj)
}
and
Waiter(find,move,link,queue) =
select {
find?(mymove,myfind).(Waiter(find,move,myfind,queue) |
link!(mymove,find));
move?(obj).Owner(find,move,link,queue,obj)
};
A very simple system is then defined, considering only three nodes.
defproc
Dir =
new find1,move1,find2,move2,find3,move3,obj in
( obj!() |
TerminalOwner(find1,move1,obj) |
Idle(find2,move2,find1) |
Idle(find3,move3,find2));
We then check to see if the system has always a reduction in every possible configuration.
defprop deadlockfree = always(<>true); check Dir |= deadlockfree;We now intend to verify that the object will be possibly acquired, in exclusive mode, by any node.
defprop object(s) = < s! > 0; defprop node(f) = 1 and (fresh a. fresh b. < f?(a,b) > true);We then state that for a node to own the object it just has to contain an occurrence of the object's
defprop owns(i,obj) = (node(i) and @obj); defprop exclusive(i,obj) = (owns(i,obj) | not @obj);Finally we specify our liveness property by stating that all nodes in the system can come to acquire
defprop live = hidden obj.
inside (object(obj) |
forall i. ((node(i) | true) => eventually exclusive(i,obj)));
We check to see if the system satisfies this property for every possible configuration.
check Dir |= always(live);