• Nenhum resultado encontrado

Dynamic Analysis

No documento Software Abstractions (páginas 196-200)

6: Examples

6.1 Leader Election in a Ring

6.1.4 Dynamic Analysis

It’s good to start with a simple simulation, to check that the model isn’t overconstrained. For example, we might ask to see an execution in which some process gets elected:

pred show () { some elected }

run show for 3 Process, 4 Time

We’ve picked a scope of three processes—the smallest interesting ring—

and four times, because the leader’s token will have to go all the way around, so there must be at least one more time instant than processes.

A sample trace generated by the analyzer is shown in fig. 6.4: the iden-tifier of process P2 goes all the way round, before any other identifiers have been sent.

Having established that the model is at least consistent, we might move on to checking some properties. The purpose of the protocol is to reach a state in which exactly one leader is elected. When possible, it’s best to split a property into subproperties and check them individually. This makes it easier to diagnose what went wrong if a property doesn’t hold.

So we’ll consider two properties separately: that there be at most and at least one elected process.

Here is an assertion claiming that there is at most one elected process:

assert AtMostOneElected { lone elected.Time }

check AtMostOneElected for 3 Process but 7 Time

The expression elected.Time denotes the set of processes elected at any time, so the assertion says not only that there is at most one process elected at any time, which could have been written

all t: Time | lone elected.t

but, more strongly, that the election doesn’t change from one process to another. The scope in this assertion limits the analysis to a ring of 3

fig. 6.4 A sample trace for ring election: the initial state is in the panel at the top left; execution proceeds through the

panels clockwise.

examples 11 processes and 7 time instants. The AtMostOneElected assertion is valid, and no counterexamples are found.

Here is the assertion claiming that there is at least one elected process:

assert AtLeastOneElected { some t: Time | some elected.t }

check AtLeastOneElected for 3 but 7 Time

This second assertion is invalid; it has a counterexample in which noth-ing happens at all. The problem is includnoth-ing the skip operation, which allows every process to skip in every step!

To fix this problem, we can force progress by insisting that whenever some process has a nonempty identifier pool, some process (not neces-sarily the same one) must make a move. We write this as a predicate

pred progress () { all t: Time - TO/last() | let t’ = TO/next (t) |

some Process.toSend.t =>

some p: Process | not skip (t, t’, p) }

and then condition the assertion on this predicate holding:

assert AtLeastOneElected {

progress () => some Elected.Time }

check AtLeastOneElected for 3 Process, 7 Time

The scope of 7 time instants is actually the smallest that is guaranteed to produce a leader. To find this scope, I simply started with a smaller scope and increased it until no counterexample was generated for the assertion.

Discussion

Are the processes always placed in the ring in the order of their process identifiers?

No. They appear in that order in fig. 6.4 because of the Alloy Analyzer’s symmetry-breaking optimization (see the discussion following section 5.2.1). Since atoms are interchangeable, you can take any instance (or counterexample) of a command and create another one by permuting the atoms. A mathematician would say “there is no loss of generality”

in ordering the processes around the ring P0, P1, P2, etc., because the description of the scheme never refers to particular atoms.

The analyzer exploits this to reduce the search, by imposing a constraint on succ. The same trick is used in the ordering relation of the module util/ordering: this is why time instants in traces always come out in order.

If the model explicitly compared the two relations, it would no longer be valid to break symmetry in both cases, so the analyzer will back off, and no longer include the symmetry-breaking constraint on succ. To see this, add

fact DifferentOrder {

all p: Process | p.succ != PO/next(p) }

and the simulation will show a ring in which the identifiers appear out of order.

How can AtLeastOneElected be valid? Doesn’t the scope allow shorter trac-es?

The symmetry breaking associated with the ordering module (men-tioned in the discussion following subsection 6.1.1) actually forces the ordered set to contain the maximum number of atoms the scope per-mits. So our two commands have the same effect they would have if written with an exact scope:

check AtMostOneElected for exactly 3 Process, exactly 7 Time check AtLeastOneElected for exactly 3 Process, exactly 7 Time Is this a violation of scope monotonicity? If so, does it matter?

Yes, it is a violation, and yes, it matters (at least in some respects). For checking AtLeastOneElected, the exact scope is necessary for the signa-ture Time; this kind of eventuality property is never scope monotonic.

For checking AtMostOneElected, on the other hand, the exact scope is not desirable, because it’s conceivable that there are bad traces (in which two processes get elected) that cannot be extended to the full length required by the scope. Unfortunately, it’s not possible to check that this doesn’t happen (see section 5.3).

Another undesirable consequence of the exact scope, this time for both commands, is that it forces an exact number of processes in the ring. It would be easy, however, to adjust the model so that analysis in a scope of k considers all rings with up to k processes, by introducing a subsig-nature like this:

examples 1 sig Process {}

sig RingProcess extends Process { succ: RingProcess,

toSend: RingProcess -> Time, elected: set Time

}

fact Ring {all p: RingProcess | RingProcess in p.^succ}

thereby imposing the multiplicity constraint of succ and the fact ring only on the subset of processes that appear in the ring.

No documento Software Abstractions (páginas 196-200)