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);