SPIN VERIFICATION EXAMPLES AND EXERCISES

Google

SPIN VERIFICATION EXAMPLES AND EXERCISES

Included in this memo are some verification exercises that can help new users to get acquainted with Spin. A few example models for standard verification problems are included at the end. All examples are also available as Promela files in the Test directory of the Spin distribution (they are bundled in the shell archive Test/examples in the main distribution).

1. Evaluating Search Complexity

This first exercise is meant to give an indication of the nature of the state space explosion phenomenon, and how Spin deals with it. It consists of eight small problems that are solved using Spin. The problems are numbered [1.a] through [1.h]. At each step, it is best to first try to predict what should happen, to write down these predictions, to then perform the experiment, and explain the results.

[1.a] How many reachable states do you predict will the following naive Promela model generate?


	init {	/* file: ex.1a */

		byte i = 0;

		do

		:: i = i+1

		od

	}

Try a simulation run:

	$ spin -p -l ex.1a	# print out local vars at every step

	...

Will the simulation terminate? []

[1.b] Estimate the total number of reachable states that should be inspected in an exhaustive verification. Is it a finite number? Will a verification run terminate? Try it as follows.


	$ spin -a ex.1a

	$ cc -o pan pan.c

	$ pan

	...

Explain the output. []

[1.c] What would happen if you had declared the variable to be a short instead of a byte ? What if you use an int ? [Hint: use Table 1 in Manual.html]

Try either of them with a verification run. How do you explain the result? [Hint: check the analyzer's defaults by saying pan -? ] []

[1.d] Next, predict how many reachable states there are for the following system. Write them down as a complete reachability tree.


	#define N	2

	init {	/* file: ex.1b */

		chan dummy = [N] of { byte };

		do

		:: dummy!85

		:: dummy!170

		od

	}

Check your prediction as follows. We're only interested in the size of the state space, not in the obvious problems caused by buffer overflow, so we use the -m option from Spin to define that buffer overflow is to be ignored. (Messages appended to a full buffer are then lost.)

	$ spin -m -a ex.1b	# use -m to ignore buffer overflow

	$ cc -o pan pan.c

	$ pan

	...

Explain the result. []

[1.e] What happens if you set N to 3 ? Express the number of states as a function of N. Use the formula to calculate how many states there will be if you set N to 14 ? Check your prediction as follows.


	$ spin -m -a ex.1b	# use -m to ignore buffer overflow

	$ cc -o pan pan.c	# optional: use the optimizer -O

	$ time pan

	...


Write down:

	T: the sum of user time plus system time for the run

	S: the number of states stored

	G: the number of total number of states generated and analyzed

	V: the vector-size (the amount of memory needed to store one state)



G/T gives you a measure for the efficiency of the run, and

S*V gives you the amount of memory that is needed to store

the state space without compression.

This is not the only place where memory that is used during the search (the stack also consumes memory) but it is typically the largest memory requirement. []

[1.f] The efficiency of the conventional reachability analysis is determined by the state space storage functions. To study this, repeat the last verification run with a smaller and a bigger hash table for storing reachable states:


	$ time pan -w10		# hash table with 210 slots

	...

	$ time pan -w20		# hash table with 220 slots

	...

Explain the results. [Hint: compare the number of hash conflicts.] []

[1.g] How much memory would you need to do a run with N=20 ? (Warning: both the number of reachable states and the number of bytes per state goes up with N. Estimate about 30 bytes per state for N=20.) Assuming that you have about 8 Megabyte on your system, what maximal fraction of the state-space would you expect to be able to analyze ?

Now set N to 20 and use the bitstate storage option, as follows.


	$ spin -m -a ex.1b		# as before

	$ cc -DBITSTATE -o pan pan.c	# different

	$ time pan

	...

If you did the calculation, you probably estimated that there should be 2,097,151 reachable system states for N=20. What percentage of these states was reached in the new run? How much memory was used [Hint: cf. [1.h] below] ? (Compare to the earlier estimated maximal coverage for a conventional verification and explain the difference.) []

[1.h] The default bitstate space, used above, has 222 bits (i.e., 218 bytes, or about one quarter Megabyte) repeat the run with different amount of memory to get different coverage. Check what percentage of the number of states is reached when you use the 8 Megabyte state space on which your first estimate for maximal coverage in a full state space search was based (223 bytes is 226 bits, which means a runtime flag -w26 )

You should be able to get close to 99% coverage and between 4,000 and 40,000 states analyzed per second. For comparison, the speed on a DEC/VAX-8550 is roughly 16,000 s/s.; on a 30 MIPS Silicon Graphics machine 40,000 s/s; on a Sun 3/60 4,500 s/s. []

2. Verification of a Protocol

Figure 2 shows a protocol specification given by Bartlett et al. as Figure 3c in their paper introducing the alternating-bit protocol. []

The Promela version of this protocol is shown below. Use Spin to check if it has the property that ``Every message fetched by A is received error-free at least once and accepted at most once by B.''


  1 #define MAX 4                /* file ex.2 */

  2 proctype A(chan in, out)

  3 {	byte mt; /* message data */

  4 	bit  vr;

  5 S1:	mt = (mt+1)%MAX;

  6 	out!mt,1;

  7 	goto S2;

  8 S2:	in?vr;

  9 	if

 10 	:: (vr == 1) -> goto S1

 11 	:: (vr == 0) -> goto S3

 12 	:: printf("MSC: AERROR1\n") -> goto S5

 13 	fi;

 14 S3:	out!mt,1;

 15 	goto S2;

 16 S4:	in?vr;

 17 	if

 18 	:: goto S1

 19 	:: printf("MSC: AERROR2\n"); goto S5

 20 	fi;

 21 S5:	out!mt,0;

 22 	goto S4

 23 }

 24 proctype B(chan in, out)

 25 {	byte mr, lmr;

 26 	bit ar;

 27 	goto S2; /* initial state */

 28 S1:	assert(mr == (lmr+1)%MAX);

 29 	lmr = mr;

 30 	out!1;

 31 	goto S2;

 32 S2:	in?mr,ar;

 33 	if

 34 	:: (ar == 1) -> goto S1

 35 	:: (ar == 0) -> goto S3

 36 	:: printf("MSC: ERROR1\n"); goto S5

 37 	fi;

 38 S3:	out!1;

 39 	goto S2;

 40 S4:	in?mr,ar;

 41 	if

 42 	:: goto S1

 43 	:: printf("MSC: ERROR2\n"); goto S5

 44 	fi;

 45 S5:	out!0;

 46 	goto S4

 47 }

 48 init {

 49 	chan a2b = [2] of { bit };

 50 	chan b2a = [2] of { byte, bit };

 51 	atomic {

 52 		run A(a2b, b2a);

 53 		run B(b2a, a2b)

 54 	}

 55 }

3. Verification of an Interface Standard

CCITT Recommendation X.21 was one of the first protocols shown to be incompletely specified with an automated analysis. The verification was performed in 1977 by Colin West.

What in 1977 was described as a ``reasonably complex protocol'' is today a rather simple litmus test for automated protocol verifiers, that shouldn't take more than a few milli-seconds of CPU time. The Promela model below is derived from Figure 3, which is based on the specification used by West. (``all'' in Figure 3 means ``all other states.'')


  1 mtype = { a, b, c, d, e, i, l, m, n, q, r, u, v };

  2                                   /* file ex.3 */

  3 chan dce = [0] of { mtype };

  4 chan dte = [0] of { mtype };

  5 

  6 active proctype dte_proc()

  7 {

  8 state01:

  9 	do

 10 	:: dce!b -> /* state21 */ dce!a

 11 	:: dce!i -> /* state14 */

 12 			if

 13 			:: dte?m -> goto state19

 14 			:: dce!a

 15 			fi

 16 	:: dte?m -> goto state18

 17 	:: dte?u -> goto state08

 18 	:: dce!d -> break

 19 	od;

 20 

 21 	/* state02: */

 22 	if

 23 	:: dte?v

 24 	:: dte?u -> goto state15

 25 	:: dte?m -> goto state19

 26 	fi;

 27 state03:

 28 	dce!e;

 29 	/* state04: */

 30 	if

 31 	:: dte?m -> goto state19

 32 	:: dce!c

 33 	fi;

 34 	/* state05: */

 35 	if

 36 	:: dte?m -> goto state19

 37 	:: dte?r

 38 	fi;

 39 	/* state6A: */

 40 	if

 41 	:: dte?m -> goto state19

 42 	:: dte?q

 43 	fi;

 44 state07:

 45 	if

 46 	:: dte?m -> goto state19

 47 	:: dte?r

 48 	fi;

 49 	/* state6B: */

 50 	if		/* non-deterministic select */

 51 	:: dte?q -> goto state07

 52 	:: dte?q

 53 	:: dte?m -> goto state19

 54 	fi;

 55 	/* state10: */

 56 	if

 57 	:: dte?m -> goto state19

 58 	:: dte?r

 59 	fi;

 60 state6C:

 61 	if

 62 	:: dte?m -> goto state19

 63 	:: dte?l

 64 	fi;

 65 	/* state11: */

 66 	if

 67 	:: dte?m -> goto state19

 68 	:: dte?n

 69 	fi;

 70 	/* state12: */

 71 	if

 72 	:: dte?m -> goto state19

 73 	:: dce!b -> goto state16

 74 	fi;

 75 state15:

 76 	if

 77 	:: dte?v -> goto state03

 78 	:: dte?m -> goto state19

 79 	fi;

 80 state08:

 81 	if

 82 	:: dce!c

 83 	:: dce!d -> goto state15

 84 	:: dte?m -> goto state19

 85 	fi;

 86 	/* state09: */

 87 	if

 88 	:: dte?m -> goto state19

 89 	:: dte?q

 90 	fi;

 91 	/* state10B: */

 92 	if

 93 	:: dte?r -> goto state6C

 94 	:: dte?m -> goto state19

 95 	fi;

 96 state18:

 97 	if

 98 	:: dte?l -> goto state01

 99 	:: dte?m -> goto state19

100 	fi;

101 state16:

102 	dte?m;

103 	/* state17: */

104 	dte?l;

105 	/* state21: */

106 	dce!a; goto state01;

107 state19:

108 	dce!b;

109 	/* state20: */

110 	dte?l;

111 	/* state21: */

112 	dce!a; goto state01

113 }

114 active proctype dce_proc()

115 {

116 state01:

117 	do

118 	:: dce?b -> /* state21 */ dce?a

119 	:: dce?i -> /* state14 */

120 			if

121 			:: dce?b -> goto state16

122 			:: dce?a

123 			fi

124 	:: dte!m -> goto state18

125 	:: dte!u -> goto state08

126 	:: dce?d -> break

127 	od;

128 

129 	/* state02: */

130 	if

131 	:: dte!v

132 	:: dte!u -> goto state15

133 	:: dce?b -> goto state16

134 	fi;

135 state03:

136 	dce?e;

137 	/* state04: */

138 	if

139 	:: dce?b -> goto state16

140 	:: dce?c

141 	fi;

142 	/* state05: */

143 	if

144 	:: dce?b -> goto state16

145 	:: dte!r

146 	fi;

147 	/* state6A: */

148 	if

149 	:: dce?b -> goto state16

150 	:: dte!q

151 	fi;

152 state07:

153 	if

154 	:: dce?b -> goto state16

155 	:: dte!r

156 	fi;

157 	/* state6B: */

158 	if		/* non-deterministic select */

159 	:: dte!q -> goto state07

160 	:: dte!q

161 	:: dce?b -> goto state16

162 	fi;

163 	/* state10: */

164 	if

165 	:: dce?b -> goto state16

166 	:: dte!r

167 	fi;

168 state6C:

169 	if

170 	:: dce?b -> goto state16

171 	:: dte!l

172 	fi;

173 	/* state11: */

174 	if

175 	:: dce?b -> goto state16

176 	:: dte!n

177 	fi;

178 	/* state12: */

179 	if

180 	:: dce?b -> goto state16

181 	:: dte!m -> goto state19

182 	fi;

183 state15:

184 	if

185 	:: dte!v -> goto state03

186 	:: dce?b -> goto state16

187 	fi;

188 state08:

189 	if

190 	:: dce?c

191 	:: dce?d -> goto state15

192 	:: dce?b -> goto state16

193 	fi;

194 	/* state09: */

195 	if

196 	:: dce?b -> goto state16

197 	:: dte!q

198 	fi;

199 	/* state10B: */

200 	if

201 	:: dte!r -> goto state6C

202 	:: dce?b -> goto state16

203 	fi;

204 state18:

205 	if

206 	:: dte!l -> goto state01

207 	:: dce?b -> goto state16

208 	fi;

209 state16:

210 	dte!m;

211 	/* state17: */

212 	dte!l;

213 	/* state21: */

214 	dce?a; goto state01;

215 state19:

216 	dce?b;

217 	/* state20: */

218 	dte!l;

219 	/* state21: */

220 	dce?a; goto state01

221 }

Verify the X.21 protocol using Spin. []

4. Verification of Mutual Exclusion Algorithms

If two or more concurrent processes execute the same code and access the same data, there is a potential problem that they may overwrite each others results and corrupt the data. The mutual exclusion problem is the problem of restricting access to a critical section in the code to a single process at a time, assuming only the indivisibility of read and write instructions. (The problem disappears if one can assume an indivisible test-and-set instruction.) The problem and a first solution were first published by Dijkstra.

[4.a] The following `improved' solution appeared one year later in the same journal (Comm. of the ACM, Vol. 9, No. 1, p. 45) by another author. It is reproduced here as it was published (in pseudo Algol).


  1 Boolean array b(0;1) integer k, i, j,

  2 comment process i, with i either 0 or 1 and j = 1-i;

  3 C0:	b(i) := false;

  4 C1:	if k != i then begin

  5 C2:	if not (b(j) then go to C2;

  6 	else k := i; go to C1 end;

  7 	else critical section;

  8 	b(i) := true;

  9 	remainder of program;

 10 	go to C0;

 11 	end

Model the solution in Promela, and proof or disproof the correctness of the algorithm. []

[4.b] The problem continues to be popular. For an overview solution attempts up to roughly 1984, see Raynal or Lamport. For two processes, for instance, a particularly elegant solution was published by Peterson. In Promela the solution can be modeled as follows. The predefined variable \(CW_pid contains the id of the running process: in this case either 0 or 1.

  1 #define true	1                      /* file ex.4b */

  2 #define false	0

  3 bool flag[2];

  4 bool turn;

  5 active [2] proctype user()

  6 {	flag[_pid] = true;

  7 	turn = _pid;

  8 	(flag[1-_pid] == false || turn == 1-_pid);

  9 crit:	skip;	/* critical section */

 10 	flag[_pid] = false

 11 }

Prove Peterson's algorithm correct with Spin. []

[4.c] Despite all the publications, there is no shortage of faulty solutions to the mutual exclusion problem. The next version was recommended by a computer manufacturer to a customer.

  1 byte in;                                    /* file ex.4c */

  2 byte x, y, z;

  3 active [2] proctype user()

  4 {	byte me = _pid+1;			/* me is 1 or 2 */

  5 L1:	x = me;

  6 L2:	if

  7 	:: (y != 0 && y != me) -> goto L1	/* try again */

  8 	:: (y == 0 || y == me)

  9 	fi;

 10 L3:	z = me;

 11 L4:	if

 12 	:: (x != me)  -> goto L1		/* try again */

 13 	:: (x == me)

 14 	fi;

 15 L5:	y = me;

 16 L6:	if

 17 	:: (z != me) -> goto L1			/* try again */

 18 	:: (z == me)

 19 	fi;

 20 L7:						/* success */

 21 	in = in+1;

 22 	assert(in == 1);

 23 	in = in - 1;

 24 	goto L1

 25 }

First try to find a scenario that leads to the assertion violation by studying the algorithm. Next generate the counter-example with Spin. []

5. Verification of Petri Nets

It is often easier to build an little verification model and mechanically verify it than it is to write or to check a manual proof of correctness.

Petri Nets are relatively easy to model as Promela verification models. A Promela model for the net in Figure 4, for instance, is shown below.

  1 #define Place	byte    /* assume < 256 tokens per place */

  2                             /* file ex.5a */

  3 Place p1, p2, p3;

  4 Place p4, p5, p6;

  5 #define inp1(x)		(x>0) -> x=x-1

  6 #define inp2(x,y)	(x>0&&y>0) -> x = x-1; y=y-1

  7 #define out1(x)		x=x+1

  8 #define out2(x,y)	x=x+1; y=y+1

  9 init

 10 {	p1 = 1; p4 = 1;	/* initial marking */

 11 	do

 12 /*t1*/	:: atomic { inp1(p1)	-> out1(p2) }

 13 /*t2*/	:: atomic { inp2(p2,p4)	-> out1(p3) }

 14 /*t3*/	:: atomic { inp1(p3)	-> out2(p1,p4) }

 15 /*t4*/	:: atomic { inp1(p4)	-> out1(p5) }

 16 /*t5*/	:: atomic { inp2(p1,p5) -> out1(p6) }

 17 /*t6*/	:: atomic { inp1(p6)	-> out2(p4,p1) }

 18 	od

 19 }

For this exercise, consider the Petri Net model below, which first appeared as Figure 1 in Berthelot et al.. The net was proven to be deadlock free with a manual reduction and proof technique. Verify the result with Spin. []

  1 #define Place	byte	/* assume < 256 tokens per place */

  2                             /* file ex.5b */

  3 Place P1, P2, P4, P5, RC, CC, RD, CD;

  4 Place p1, p2, p4, p5, rc, cc, rd, cd;

  5 

  6 #define inp1(x)		(x>0) -> x=x-1

  7 #define inp2(x,y)	(x>0&&y>0) -> x = x-1; y=y-1

  8 #define out1(x)		x=x+1

  9 #define out2(x,y)	x=x+1; y=y+1

 10 

 11 init

 12 {	P1 = 1; p1 = 1;	/* initial marking */

 13 	do

 14 	:: atomic { inp1(P1)	-> out2(rc,P2)	}	/* DC */

 15 	:: atomic { inp2(P2,CC)	-> out1(P4)	}	/* CA */

 16 	:: atomic { inp1(P4)	-> out2(P5,rd)	}	/* DD */

 17 	:: atomic { inp2(P5,CD)	-> out1(P1)	}	/* FD */

 18 	:: atomic { inp2(P1,RC)	-> out2(P4,cc)	}	/* AC */

 19 	:: atomic { inp2(P4,RD) -> out2(P1,cd)	}	/* AD */

 20 	:: atomic { inp2(P5,RD)	-> out1(P1)	}	/* DA */

 21 

 22 	:: atomic { inp1(p1)	-> out2(RC,p2)	}	/* dc */

 23 	:: atomic { inp2(p2,cc)	-> out1(p4)	}	/* ca */

 24 	:: atomic { inp1(p4)	-> out2(p5,RD)	}	/* dd */

 25 	:: atomic { inp2(p5,cd)	-> out1(p1)	}	/* fd */

 26 	:: atomic { inp2(p1,rc)	-> out2(p4,CC)	}	/* ac */

 27 	:: atomic { inp2(p4,rd) -> out2(p1,CD)	}	/* ad */

 28 	:: atomic { inp2(p5,rd)	-> out1(p1)	}	/* da */

 29 	od

 30 }

6. The Cambridge Ring Protocol

The model below is based on the description in Needham and Herbert's book.

  1 #define LOSS	0	/* enable or disable message loss */

  2                             /* file ex.6 */

  3 #define QSZ	5               /* length of message queues */

  4 

  5 mtype =	{ RDY, NOTRDY, DATA, NODATA, RESET };

  6 

  7 chan sender	= [QSZ] of { mtype, byte };

  8 chan recv	= [QSZ] of { mtype, byte };

  9 

 10 active proctype Sender()

 11 {	short n = -1; byte t,m;

 12 

 13 	xr sender;

 14 	xs recv;

 15 

 16 I:		/* Idle State */

 17 		do

 18 #if LOSS==1

 19 		:: sender?_,_ -> progress2: skip

 20 #endif

 21 		:: sender?RESET,0 ->

 22 ackreset:		recv!RESET,0;	/* stay in idle */

 23 			n = -1;

 24 			goto I

 25 

 26 		/* E-rep: protocol error */

 27 

 28 		:: sender?RDY,m ->		/* E-exp */

 29 			assert(m == (n+1)%4);

 30 advance:		n = (n+1)%4;

 31 			if

 32 /* buffer */		:: atomic {

 33 				printf("MSC: NEW\n");

 34 				recv!DATA,n;

 35 				goto E

 36 			   }

 37 /* no buffer */		:: recv!NODATA,n;

 38 				goto N

 39 			fi

 40 

 41 		:: sender?NOTRDY,m ->	/* N-exp */

 42 expected:		assert(m == (n+1)%4);

 43 			goto I

 44 

 45 		/* Timeout */

 46 		/* ignored (p.154, in [Needham'82]) */

 47 

 48 		:: goto reset	/* spontaneous reset */

 49 		od;

 50 E:		/* Essential element sent, ack expected */

 51 		do

 52 #if LOSS==1

 53 		:: sender?_,_ -> progress0: skip

 54 #endif

 55 		:: sender?RESET,0 ->

 56 			goto ackreset

 57 

 58 		:: sender?RDY,m ->

 59 			if

 60 			:: (m == n) ->		/* E-rep */

 61 				goto retrans

 62 			:: (m == (n+1)%4) ->	/* E-exp */

 63 				goto advance

 64 			fi

 65 

 66 		:: sender?NOTRDY,m ->	/* N-exp */

 67 			goto expected

 68 

 69 		/* Timeout */

 70 		/* a proper behaved timeout leaves part of the

 71 		   protocol specification unreachable;  to check

 72 		   less well-behaved timeout behavior, replace

 73 		   the timeout keyword below with "empty(sender)"

 74 		   to allow the retransmission at any time when the

 75 		   sender queue is empty.  even more mis-behaved

 76 		   timeouts can be checked by replacing timeout with

 77 		   "skip"  (i.e., unconditionally executable)

 78 		*/

 79 		:: timeout ->

 80 			printf("MSC: STO\n");

 81 retrans:		recv!DATA,n	/* retransmit */

 82 

 83 		:: goto reset

 84 		od;

 85 N:		/* Non-essential element sent */

 86 		do

 87 #if LOSS==1

 88 		:: sender?_,_ -> progress1: skip

 89 #endif

 90 		:: sender?RESET,0 ->

 91 			goto ackreset

 92 

 93 		:: sender?RDY,m ->		/* E-rep */

 94 			assert(m == n)		/* E-exp: protocol error */

 95 			-> recv!NODATA,n	/* retransmit and stay in N */

 96 

 97 		:: recv!DATA,n;		/* buffer ready event */

 98 			goto E

 99 

100 		:: goto reset

101 

102 		/* Timeout */

103 		/* ignored (p.154, in [Needham'82]) */

104 		od;

105 

106 reset:		recv!RESET,0;

107 		do

108 #if LOSS==1

109 		:: sender?_,_ -> progress3: skip

110 #endif

111 		:: sender?t,m ->

112 			if

113 			:: t == RESET -> n = -1; goto I

114 			:: else	/* ignored, p. 152 */

115 			fi

116 		:: timeout ->

117 			goto reset

118 		od

119 }

120 active proctype Receiver()

121 {	byte t, n, m, Nexp;

122 

123 	xr recv;

124 	xs sender;

125 

126 I:		/* Idle State */

127 		do

128 #if LOSS==1

129 		:: recv?_,_ -> progress2: skip

130 #endif

131 		:: recv?RESET,0 ->

132 ackreset:		sender!RESET,0;		/* stay in idle */

133 			n = 0; Nexp = 0;

134 			goto I

135 

136 		:: atomic { recv?DATA(m) ->	/* E-exp */

137 			assert(m == n);

138 advance:		printf("MSC: EXP\n");

139 			n = (n+1)%4;

140 			assert(m == Nexp);

141 			Nexp = (m+1)%4;

142 			if

143 			:: sender!RDY,n; goto E

144 			:: sender!NOTRDY,n; goto N

145 			fi

146 		   }

147 

148 		:: recv?NODATA(m) ->		/* N-exp */

149 			assert(m == n)

150 

151 		/* Timeout: ignored */

152 

153 		/* only the receiver can initiate a data

154 		   transfer; p. 156 [Needham'82]

155 		   this is modeled by the statement below:

156 		*/

157 		:: empty(recv) -> sender!RDY,n; goto E

158 

159 		:: goto reset

160 		od;

161 E:

162 		do

163 #if LOSS==1

164 		:: recv?_,_ -> progress1: skip

165 #endif

166 		:: recv?RESET,0 ->

167 			goto ackreset

168 

169 		:: atomic { recv?DATA(m) ->

170 			if

171 			:: ((m+1)%4 == n) ->		/* E-rep */

172 				printf("MSC: REP\n");

173 				goto retrans

174 			:: (m == n) ->			/* E-exp */

175 				goto advance

176 			fi

177 		   }

178 

179 		:: recv?NODATA(m) ->		/* N-exp  */

180 			assert(m == n);

181 			goto I

182 

183 		/* see also the matching code in the sender model;

184 		   to check less well-behaved timeout behavior for

185 		   the receiver process, replace the timeout keyword

186 		   below with "empty(recv)" (or "skip" for a real

187 		   torture test, with significant extra complexity)

188 		*/

189 		:: timeout ->

190 			printf("MSC: RTO\n");

191 retrans:		sender!RDY,n;

192 			goto E

193 

194 		:: goto reset

195 		od;

196 N:

197 		do

198 #if LOSS==1

199 		:: recv?_,_ -> progress0: skip

200 #endif

201 		:: recv?RESET,0 ->

202 			goto ackreset

203 

204 		:: recv?DATA(m) ->		/* E-rep */

205 			assert((m+1)%4 == n);	/* E-exp and N-exp: error */

206 			printf("MSC: REP\n");

207 			sender!NOTRDY,n	/* retransmit and stay in N */

208 

209 		:: sender!RDY,n ->		/* buffer ready event */

210 			goto E

211 

212 		/* Timeout: ignored */

213 

214 		:: goto reset

215 		od;

216 progress:

217 reset:		sender!RESET,0;

218 		do

219 #if LOSS==1

220 		:: recv?_,_ -> progress3: skip

221 #endif

222 		:: recv?t,m ->

223 			if

224 			:: t == RESET -> n = 0; Nexp = 0; goto I

225 			:: else	/* ignored, p. 152 */

226 			fi

227 		:: timeout ->

228 			goto reset

229 		od

230 }

7. Sleep-Wakeup Process Scheduling

The model below for implementing sleep and wakeup routines in a distributed operating systems kernel is based on Ruane's description. The model contains an error that can be detected as a fair non-progress cycle (use runtime options -l and -f). Try to determine the cause of the error, and how to fix it. (It can be repaired by the addition of one single statement in the client process.)

  1 mtype = { Wakeme, Running };        /* file ex.7 */

  2 

  3 bit	lk,	sleep_q;

  4 bit	r_lock,	r_want;

  5 mtype	State =	Running;

  6 

  7 active proctype client()

  8 {

  9 sleep:					/* sleep routine */

 10 	atomic { (lk == 0) -> lk = 1 };	/* spinlock(&lk) */

 11 	do				/* while r->lock */

 12 	:: (r_lock == 1) ->		/* r->lock == 1 */

 13 		r_want = 1;

 14 		State = Wakeme;

 15 		lk = 0;			/* freelock(&lk) */

 16 		(State == Running);	/* wait for wakeup */

 17 	:: else ->			/* r->lock == 0 */

 18 		break

 19 	od;

 20 progress:

 21 	assert(r_lock == 0);		/* should still be true */

 22 	r_lock = 1;			/* consumed resource */

 23 	lk = 0;				/* freelock(&lk) */

 24 	goto sleep

 25 }

 26 

 27 active proctype server()		/* interrupt routine */

 28 {

 29 wakeup:					/* wakeup routine */

 30 	r_lock = 0;			/* r->lock = 0 */

 31 	(lk == 0);			/* waitlock(&lk) */

 32 	if

 33 	:: r_want ->			/* someone is sleeping */

 34 		atomic {		/* spinlock on sleep queue */

 35 			(sleep_q == 0) -> sleep_q = 1

 36 		};

 37 		r_want = 0;

 38 #ifdef PROPOSED_FIX

 39 		(lk == 0);		/* waitlock(&lk) */

 40 #endif

 41 		if

 42 		:: (State == Wakeme) ->

 43 			State = Running;

 44 		:: else ->

 45 		fi;

 46 		sleep_q = 0

 47 	:: else ->

 48 	fi;

 49 	goto wakeup

 50 }

8. A Leader Election Algorithm

The model below follows the description of the leader election protocol in a unidirectional ring from Dolev et al. as it is discussed and formalized by Raynal.


  1 #define N	5	/* nr of processes (use 5 for demos) */

  2 #define I	3	/* node given the smallest number    */

  3 #define L	10	/* size of buffer  (>= 2*N) */

  4                     /* file ex.8 */

  5 mtype = { one, two, winner };

  6 chan q[N] = [L] of { mtype, byte};

  7 

  8 byte nr_leaders = 0;

  9 

 10 proctype node (chan in, out; byte mynumber)

 11 {	bit Active = 1, know_winner = 0;

 12 	byte nr, maximum = mynumber, neighbourR;

 13 

 14 	xr in;	/* exclusive recv access to channel in */

 15 	xs out;	/* exclusive send access to channel out */

 16 

 17 	printf("MSC: %d\n", mynumber);

 18 	out!one(mynumber);

 19 end:	do

 20 	:: in?one(nr) ->

 21 		if

 22 		:: Active -> 

 23 			if

 24 			:: nr != maximum ->

 25 				out!two(nr);

 26 				neighbourR = nr

 27 			:: else ->

 28 				/* max is greatest number */

 29 				assert(nr == N);

 30 				know_winner = 1;

 31 				out!winner,nr;

 32 			fi

 33 		:: else ->

 34 			out!one(nr)

 35 		fi

 36 

 37 	:: in?two(nr) ->

 38 		if

 39 		:: Active -> 

 40 			if

 41 			:: neighbourR > nr && neighbourR > maximum ->

 42 				maximum = neighbourR;

 43 				out!one(neighbourR)

 44 			:: else ->

 45 				Active = 0

 46 			fi

 47 		:: else ->

 48 			out!two(nr)

 49 		fi

 50 	:: in?winner,nr ->

 51 		if

 52 		:: nr != mynumber ->

 53 			printf("MSC: LOST\n");

 54 		:: else ->

 55 			printf("MSC: LEADER\n");

 56 			nr_leaders++;

 57 			assert(nr_leaders == 1)

 58 		fi;

 59 		if

 60 		:: know_winner

 61 		:: else -> out!winner,nr

 62 		fi;

 63 		break

 64 	od

 65 }

 66 

 67 init {

 68 	byte proc;

 69 	atomic {

 70 		proc = 1;

 71 		do

 72 		:: proc <= N ->

 73 			run node (q[proc-1], q[proc%N], (N+I-proc)%N+1);

 74 			proc++

 75 		:: proc > N ->

 76 			break

 77 		od

 78 	}

 79 }

9. A Go-Back-N Sliding Window Protocol

This go-back-n sliding window protocol follows the description from Tanenbaum. The model below includes some annotations to facilitate simulations.


  1 #define MaxSeq	3	/* window size */

  2 #define Wrong(x)	x = (x+1) % (MaxSeq)

  3 #define Right(x)	x = (x+1) % (MaxSeq + 1)

  4 #define inc(x)		Right(x)

  5                                             /* file ex.9 */

  6 chan q[2] = [MaxSeq] of { byte, byte };	/* message passing channel */

  7 

  8 active [2] proctype p5()	/* starts two copies of proctype p5 */

  9 {	byte	NextFrame, AckExp, FrameExp, r, s, nbuf, i;

 10 	chan	in, out;

 11 	in  = q[_pid];

 12 	out = q[1-_pid];

 13 	xr in; xs out;		/* partial order reduction claims */

 14 

 15 	do

 16 	:: nbuf < MaxSeq ->	/* outgoing messages */

 17 		nbuf++;

 18 		out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 19 		inc(NextFrame)

 20 

 21 	:: q[_pid]?r,s ->	/* incoming messages */

 22 		if

 23 		:: r == FrameExp ->

 24 			printf("MSC: accept %d\n", r);

 25 			inc(FrameExp)

 26 		:: else /* ignore message */

 27 		fi;

 28 		do

 29 		:: ((AckExp <= s) && (s <  NextFrame))

 30 		|| ((AckExp <= s) && (NextFrame <  AckExp))

 31 		|| ((s <  NextFrame) && (NextFrame <  AckExp)) ->

 32 			nbuf--;

 33 			inc(AckExp)

 34 		:: else -> break

 35 		od

 36 

 37 	:: timeout ->		/* retransmission timeout */

 38 		NextFrame = AckExp;

 39 		printf("MSC: timeout\n");

 40 		i = 1;

 41 		do

 42 		:: i <= nbuf ->

 43 			out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 44 			inc(NextFrame);

 45 			i++

 46 		:: else -> break

 47 		od

 48 	od

 49 }

When running the verification with partial order reduction enabled (the default), the verifier will complain about a suspected violation of the exclusive channel access assertions (the xr and xs statements). In this case, the complaint is unjustified. It is caused by the attempt to make the code for sender and receiver equal, and the use of a channel array q[]. The complaint can be supressed by compiling as follows:
$ spin -a ex.9

$ cc -DSAFETY -DXUSAFE -o pan pan.c

$ pan -m40000

. . .

The directive -DSAFETY says that we are only interested in safety properties (not LTL properties or cycles). The directive -DXUSAFE tells the verifier that the xr and xs assertions need not be checked.

To reduce the complexity of the model, without affecting its scope, one can add d_step and atomic sequences to the model, as shown in the modified version of this code below.

  1 #define MaxSeq	3                     /* file ex.9b */

  2 #define MaxSeq_plus_1	4

  3 #define inc(x)	x = (x + 1) % (MaxSeq + 1)

  4 

  5 chan q[2] = [MaxSeq] of { byte, byte };

  6 

  7 active [2] proctype p5()

  8 {	byte	NextFrame, AckExp, FrameExp,

  9 		r, s, nbuf, i;

 10 	chan in, out;

 11 

 12 	d_step {

 13 		in = q[_pid];

 14 		out = q[1-_pid]

 15 	};

 16 	xr in;

 17 	xs out;

 18 

 19 	do

 20 	:: atomic { nbuf < MaxSeq ->

 21 		nbuf++;

 22 		out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 23 		printf("MSC: nbuf: %d\n", nbuf);

 24 		inc(NextFrame)

 25 	}

 26 	:: atomic { in?r,s ->

 27 		if

 28 		:: r == FrameExp ->

 29 			printf("MSC: accept %d\n", r);

 30 			inc(FrameExp)

 31 		:: else

 32 			-> printf("MSC: reject\n")

 33 		fi

 34 	};

 35 	d_step {

 36 		do

 37 		:: ((AckExp <= s) && (s <  NextFrame))

 38 		|| ((AckExp <= s) && (NextFrame <  AckExp))

 39 		|| ((s <  NextFrame) && (NextFrame <  AckExp)) ->

 40 			nbuf--;

 41 			printf("MSC: nbuf: %d\n", nbuf);

 42 			inc(AckExp)

 43 		:: else ->

 44 			printf("MSC: %d %d %d\n", AckExp, s, NextFrame);

 45 			break

 46 		od; skip

 47 	}

 48 	:: timeout ->

 49 	d_step {

 50 		NextFrame = AckExp;

 51 		printf("MSC: timeout\n");

 52 		i = 1;

 53 		do

 54 		:: i <= nbuf ->

 55 			out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 56 			inc(NextFrame);

 57 			i++

 58 		:: else ->

 59 			break

 60 		od; i = 0

 61 	}

 62 	od

 63 }

Next, we may want to verify that this protocol correctly transfers data, without loss or reordering. The model can be modified as follows to perform these checks. The verifications are based on Wolper's data independence method, using three colored messages (balls). To reduce complexity, we make sure that only one sender transmits the colored balls, and the other checks that they are accepted in the proper sequence. The return direction transmits only dummy balls, and accepts them without check.
  1 #define MaxSeq	3		/* file: ex.9c */

  2 #define MaxSeq_plus_1	4

  3 #define inc(x)	x = (x + 1) % (MaxSeq + 1)

  4 

  5 #define CHECKIT

  6 

  7 #ifdef CHECKIT

  8 	mtype = { red, white, blue };	/* Wolper's test */

  9 	chan source = [0] of { mtype };

 10 	chan q[2] = [MaxSeq] of { mtype, byte, byte };

 11 	mtype rcvd = white;

 12 	mtype sent = white;

 13 #else

 14 	chan q[2] = [MaxSeq] of { byte, byte };

 15 #endif

 16 

 17 active [2] proctype p5()

 18 {	byte	NextFrame, AckExp, FrameExp,

 19 		r, s, nbuf, i;

 20 	chan in, out;

 21 #ifdef CHECKIT

 22 	mtype	ball;

 23 	byte frames[MaxSeq_plus_1];

 24 #endif

 25 

 26 	d_step {

 27 		in = q[_pid];

 28 		out = q[1-_pid]

 29 	};

 30 

 31 	xr in;

 32 	xs out;

 33 

 34 	do

 35 	:: atomic {

 36 		nbuf < MaxSeq ->

 37 		nbuf++;

 38 #ifdef CHECKIT

 39 		if

 40 		:: _pid%2 -> source?ball

 41 		:: else

 42 		fi;

 43 		frames[NextFrame] = ball;

 44 		out!ball, NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 45 		if

 46 		:: _pid%2 -> sent = ball;

 47 		:: else

 48 		fi;

 49 #else

 50 		out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

 51 #endif

 52 #ifdef VERBOSE

 53 		printf("MSC: nbuf: %d\n", nbuf);

 54 #endif

 55 		inc(NextFrame)

 56 	}

 57 #ifdef CHECKIT

 58 	:: atomic { in?ball,r,s ->

 59 #else

 60 	:: atomic { in?r,s ->

 61 #endif

 62 		if

 63 		:: r == FrameExp ->

 64 #ifdef VERBOSE

 65 			printf("MSC: accept %d\n", r);

 66 #endif

 67 #ifdef CHECKIT

 68 			if

 69 			:: _pid%2

 70 			:: else -> rcvd = ball

 71 			fi;

 72 #endif

 73 			inc(FrameExp)

 74 		:: else

 75 #ifdef VERBOSE

 76 			-> printf("MSC: reject\n")

 77 #endif

 78 		fi

 79 	};

 80 	d_step {

 81 	 	do

 82 		:: ((AckExp <= s) && (s <  NextFrame))

 83 		|| ((AckExp <= s) && (NextFrame <  AckExp))

 84 		|| ((s <  NextFrame) && (NextFrame <  AckExp)) ->

 85 			nbuf--;

 86 #ifdef VERBOSE

 87 			printf("MSC: nbuf: %d\n", nbuf);

 88 #endif

 89 			inc(AckExp)

 90 		:: else ->

 91 #ifdef VERBOSE

 92 			printf("MSC: %d %d %d\n", AckExp, s, NextFrame);

 93 #endif

 94 			break

 95 		od;

 96 		skip

 97 	}

 98 	:: timeout ->

 99 	d_step {

100 		NextFrame = AckExp;

101 #ifdef VERBOSE

102 		printf("MSC: timeout\n");

103 #endif

104 		i = 1;

105 		do

106 		:: i <= nbuf ->

107 #ifdef CHECKIT

108 			if

109 			:: _pid%2 -> ball = frames[NextFrame]

110 			:: else

111 			fi;		

112 			out!ball, NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

113 #else

114 			out!NextFrame , (FrameExp + MaxSeq) % (MaxSeq + 1);

115 #endif

116 			inc(NextFrame);

117 			i++

118 		:: else ->

119 			break

120 		od;

121 		i = 0

122 	}

123 	od

124 }

125 #ifdef CHECKIT

126 active proctype Source()

127 {

128 	do

129 	:: source!white

130 	:: source!red -> break

131 	od;

132 	do

133 	:: source!white

134 	:: source!blue -> break

135 	od;

136 end:	do

137 	:: source!white

138 	od

139 }

140 

141 #define sw	(sent == white)

142 #define sr	(sent == red)

143 #define sb	(sent == blue)

144 

145 #define rw	(rcvd == white)

146 #define rr	(rcvd == red)

147 #define rb	(rcvd == blue)

148 

149 #define LTL	3

150 #if LTL==1

151 

152 never {		/* ![](sr -> <> rr) */

153 /* the claims were produced by spin -f and edited */

154         do

155         :: 1

156         :: !rr && sr -> goto accept

157         od;

158 accept:

159         if

160         :: !rr -> goto accept

161         fi

162 }

163 

164 #endif

165 #if LTL==2

166 

167 never {		/* !rr U rb */

168 	do

169         :: !rr

170         :: rb -> break	/* move to implicit 2nd state */

171         od

172 }

173 

174 #endif

175 #if LTL==3

176 

177 never {		/* (![](sr -> <> rr)) || (!rr U rb) */

178 

179         if

180         :: 1 -> goto T0_S5

181         :: !rr && sr -> goto accept_S8

182         :: !rr -> goto T0_S2

183         :: rb -> goto accept_all

184         fi;

185 accept_S8:

186         if

187         :: !rr -> goto T0_S8

188         fi;

189 T0_S2:

190         if

191         :: !rr -> goto T0_S2

192         :: rb -> goto accept_all

193         fi;

194 T0_S8:

195         if

196         :: !rr -> goto accept_S8

197         fi;

198 T0_S5:

199         if

200         :: 1 -> goto T0_S5

201         :: !rr && sr -> goto accept_S8

202         fi;

203 accept_all:

204         skip

205 }

206 #endif

207 

208 #endif

References

  • Bartlett, K.A., Scantlebury, R.A., and Wilkinson, P.T. `A note on reliable full-duplex transmission over half-duplex lines,' Comm. of the ACM, 1969, Vol. 12, No. 5, 260-265, Figure 3c.
  • C.H. West, `General technique for communications protocol validation,' IBM J. Res. Develop., 1978, Vol. 22, No. 3, pp. 393-404.
  • E.W. Dijkstra, `Solution to a problem in concurrent programming control,' Comm. of the ACM, 1965, Vol 8, No. 9, p. 569.
  • M. Raynal, Algorithms for Mutual Exclusion, MIT Press, Cambridge, Mass., 1986, 107 pgs. (The 1986 edition is a translation from the original French version published in 1984.)
  • L. Lamport, `The mutual exclusion problem -- parts I and II,' Journal of the ACM, Vol. 33, No. 2, April 1986, pp. 313-347.
  • G.L. Peterson, `Myths about the mutual exclusion problem,' Inf. Proc. Letters, 1981, Vol. 12, No. 3, pp. 115-116.
  • G. Berthelot, and R. Terrat, `Petri Net Theory for the correctness of protocols,' IEEE Trans. on Comm., Vol COM-30, No. 12, Dec. 1982, pp. 2497-2505.
  • R.M. Needham, A.J. Herbert, The Cambridge Distributed Computing System, Addison-Wesley Publ., London, 1982, p. 154.
  • L.M. Ruane, `Process synchronization in the UTS kernel,' Computing Systems, (Usenix, Summer 1990), Vol. 3, No. 3, pp. 387--421.
  • D. Dolev, M. Klawe, and M. Rodeh, `An O(n log n) unidirectional distributed algorithm for extrema finding in a circle,' Journal of Algorithms, Vol 3. (1982), pp. 245-260.
  • M. Raynal, Distributed algorithms and protocols, John Wiley and Sons, New York, 1992.
  • A. Tanenbaum, Computer Networks, 2nd Ed., Prentice Hall, Englewood Cliffs, NJ., 1989, p. 214, 232-233.
  • P. Wolper, ``Specifying interesting properties of programs in propositional temporal logic,'' Proc. 13th ACM Symposium on Principles of Programming Languages, St. Petersburg Beach, Fla., January 1986, pp. 148-193.


Spin Online References
Promela Manual Index
Promela Grammar
Spin HomePage
(Page Updated: 26 August 1997)