380 likes | 502 Views
Concurrency. Concurrency. A process is a program executing on a virtual computer Processor speed and multiplexing of shared resources are ignored Order of thread execution is non-deterministic Multiprocessing
E N D
Concurrency • A process is a program executing on a virtual computer • Processor speed and multiplexing of shared resources are ignored • Order of thread execution is non-deterministic • Multiprocessing • A system may contain multiple processors on which cooperating threads/processes can execute simultaneously • Multi-programming • Thread/process execution can be interleaved on a single processor because of time-slicing
The Basic Issue • Operations are not atomic • An atomic operation is one that executes to completion or does not execute at all • An atomic operation has “an all or nothing” flavor: • Either it executes to completion, or • Does not execute at all, and • No one can see a partially-executed state
Critical Sections • A critical section is: • Consecutive program instructions • all instruction executes atomically • A critical section implementation must allow only one thread to execute in the critical section at any given time • A good implementation • Allows maximum concurrency while preserving correctness
Implementation • Permit access to shared variables only within a critical section • General program structure Entry section Wait if already locked “Lock” Critical section code Exit critical section “Unlock”
Properties • Concurrent programs are specified using properties, which is a predicate that evaluated over a run of the concurrent program. • Thus, it has the value true or false in each run of the program. • We say that the property holds if it is true in each run. • A property: the value of x is always at least as large as the value of y, and x has the value of 0 at least once. • Not a property: the average number of processes waiting on a lock is less than 1.
Safety and liveness • Any property is either • a safety property, • A liveness property, or • a conjunction of a safety and a liveness property.
Safety • A safety property is of the form nothing bad happens (i.e., all states are safe). • Examples: • The number of processes in a critical section is always less than 2. • Let p be the sequence of produced values and c be the sequence of consumed values. c is always a prefix of p.
Liveness • A liveness property is of the form something good happens(i.e., a state is eventually achieved). • Examples: • A process that wishes to enter the critical section eventually enters. • p grows without bound. For every value x in p, x is eventually in c.
Safety and Liveness • Showing a safety property P holds: • find a safety property P’: P’ => P; • show that P’ initially holds; • show that each step of the program maintains P’. • Showing a liveness property holds is usually done by induction.
Basic Properties • Finite progress axiom Each process takes a step infinitely often. • Atomic shared variables Consider {x = A} any concurrent read of x will return x = B; either A or B. {x = B} {x = 0} cobegin x = x + 1; || x = x - 1; coend {x ∈ {-1, 0 1} }
Producer/Consumer • Let p be the sequence of produced values and c be the sequence of consumed values. • c is always a prefix of p. • For every value x in p, x is eventually in c. • Bounded buffer variant: • Always |p| − |c| ≤ max
Producer Active proctype producer () { do :: (turn== P) -> printf(“Produce\n”) turn = C od }
Consumer Active proctype consumer() { do :: (turn== C) -> printf(“\tConsumer\n”) turn = P od }
Correctness Conditions • A correct solution to the critical section problem must satisfy: • Safety: • Nothing bad ever happens! • At most one thread may be executing in the critical section (mutual exclusion) • Liveness: • Eventually something good happens!! • If one of more threads are in their entry section, then eventually at least one of them enters the critical section. • Further requirement: • Bounded waiting: If a thread iis in entry section, then there is a bound on the number of times that other threads are allowed to enter the critical section before thread i’ s request is granted
Step by Step • Initial value of turn is p • At least one producer will find the guard :: (turn== P) -> to be true • So, using UNIX: Spin prodcons.pml | more Produce Consume Produce Consume • Not using UNIX: Spin –u14 prodcons.pml Produce Consume Produce Consume ----------------- Depth-limit (-u14 steps) reached
Extensing the Example • Instantiate more than 1 process Active [2] proctype producer() {…} • Now we may violate CS by have 2 procucers in the CS [::(turn== P) ->]
Revised Producer Consumer Example mtype = { P,C,N}; mtype turn=P; pid who; inline request(x,y) { atomic(x=y; who=0} } Inline release(x,y) { atomic {x=y; who=0} }
Main Code Active [2] proctype producer() { do :: request (turn, P, N) -> printf (“P%d\n”,_pid); assert (who== _pid); release (turn, C) od } Active [2] proctype consumer() { do :: request (turn, C, N) -> printf (“C%d\n”,_pid); assert (who== _pid); release (turn, P) od }
Printout Spin prodcons2.pml P1 C2 P1 C3 P0 C3 P1 C3 … • There is some non determinism in the model, since both consumers share the same guard condition
Further Simulation init{ assert (false) } spin false.pml spin: line 1 “false.pml“, Error: assertion violated • A simulation is not a proof! • To prove that, invoke SPIN in verification mode spin –a prodcons2.pml #generate a verifier cc –o pan pan.c #compile the verifier
./pan #perform the verification (Spin version 4.0.7 - - 1 August 2003) Full statespace search for: never claim - (none specified) assertion violations + acceptance cycles - (not selected) invalid end state + State-vector 28 bytes, depth reached 7, errors: 0 14 states, stored 3 states, matched 17 transitions (= stored + matched) 0 atomic steps
Summary • State-space = very small • No errors • No assertion violations
Dekker's mutex algorithm bool turn, flag[2]; byte cnt; active [2] proctype mutex() /* Dekker's 1965 algorithm */ { pid i, j; i = _pid; j = 1 - _pid; again: flag[i] = true; do :: flag[j] -> if :: turn == j -> flag[i] = false; !(turn == j); flag[i] = true :: else fi :: else -> break od; cnt++; assert(cnt == 1); cnt--; /* critical section */ turn = j; flag[i] = false; Goto again }
Spin Verification spin -a mutex.pml cc -o pan pan.c ./pan (Spin Version 4.0.7 – 1 August 2003) + Partial Order Reduction Full statespace search for: never claim - (none specified) assertion violations + cycle checks - (disabled by -DSAFETY) invalid end states + State-vector 20 byte, depth reached 65, errors: 0 190 states, stored 173 states, matched 363 transitions (= stored+matched) 0 atomic steps hash conflicts: 0 (resolved) (max size 2^18 states)
Faulty Mutual Exclusion Algorithm Byte cnt; Byte x,y,z; Active[2] proctype user() { byte me= -pid+1; Again: x=me; if :: (y==0)||y==me)->skip :: else->goto again fi;
z=me; if :: (x==me)->skip :: else->goto again fi; y=me; if :: (z==me)->skip :: else->goto again fi; /* enter CS */ cnt++; assert(cnt==1); cnt--; goto again; }
Spin’s Verdict spin – a mutex_flaw.pml cc -0 pan pan.c .pan pan: assertion violated (cnt==1) (at depth 53) pan: wrote mutex_flaw.pml.trail
Bakery Algorithm proctype A() { do :: 1 -> turnA = 1; turnA = turnB + 1; (turnB == 0) || (turnA < turnB); mutex ++; mutex --; turnA = 0; od }
Dekker’s solution to the two processmutual exclusion problem #define true 1 #define false 0 #define Aturn false #define Bturn true bool x, y, t; proctype A() { x = true; t = Bturn; (y == false||t == Aturn); /*critical section*/ x = false } proctype B() { y = true; t = Aturn; (x == false || t == Bturn); /* critical section */ y = false } init { run A(); run B() }
Peterson's Mutual Exclusion bit active [2] ; bit last ; byte crit ; proctype threadMX (bit self) { do :: break :: active [self] = 1 ; last = self ; (last != self || ! active[1-self]) ; /* insufficient is: (last != self) ; */ crit ++ ; assert crit == 1 ; /* mutual exclusion */ crit -- ; active [self] = 0 ; od } init { run threadMX (0) ; run threadMX (1) ; }
Message Passing Mtype = {ini, ack, dreg, data, shutup, quiet, dead}; Chan M = [1] of {mtype}: Chan W = [1] of {mtype};
Active proctype Mproc() { W!ini; /*connection */ M?ack; /*handshake */ timeout-> /*wait */ if /*two options */ :: W!shutup /*start shutdown */ :: W!dreq /*or request data */ M?data /*receive data */ do :: W!data /*send data */ :: W!shutup; /*or shutdown */ break; od fi; M?shutup; /*shutdown handshake */ W!quiet; M?dead }
Active proctype Wproc() { W?ini; /*wait for ini */ M!ack; /*acknowledge */ do /*3 options */ :: W?dreq -> /*data requested */ M!data /*send data */ :: W?data -> /*receive data */ skip /*no response */ :: W?shutup -> M!shutup; /*start shutdown */ break; od; W?quiet; M!dead }
Spin –c protocol Proc 0 = Mproc Proc 1 = Wproc q\p 0 1 1 W!ini 1 - W?ini 2 - M!ack 2 M?ack Timeout 1 W!shutup 1 - W?shutup 2 - M! shutup 2 M?shutup 1 W!quiet 1 - W?quiet 2 - M!dead 2 M?dead …
SPIN Output proctype A state 1 -> state 2 => x = 1 state 2 -> state 3 => t = 1 state 3 -> state 4 => ((y == 0) || (t == 0)) state 4 -> state 5 => x = 0 state 5 -> state 0 => -end proctype B state 1 -> state 2 => y = 1 state 2 -> state 3 => t = 0 state 3 -> state 4 => ((x == 0) || (t == 1)) state 4 -> state 5 => y = 0 state 5 -> state 0 => -end proctype init state 1 -> state 2 => (run A()) state 2 -> state 3 => (run B()) state 3 -> state 0 => -end-
Reader-Writer #define invariant (nr == 0 || !busy) byte nr; bool busy; proctype reader(){ do :: atomic{ (!busy) -> nr=nr+1} /* reading */ nr=nr-1 od; } proctype writer (){ do ::atomic { (nr==0 && !busy) ->busy=1} /*writing*/ busy=0; od;
#define invariant (nr == 0 || !busy) byte nr; bool busy; proctype reader(){ do :: atomic{ (!busy) -> nr=nr+1} /* reading */ nr=nr-1 od; } proctype writer (){ do ::atomic { (nr==0 && !busy) ->busy=1} /*writing*/ busy=0; od; init{ nr=0; busy=0; run reader(); run reader(); run writer(); run writer(); } if :: (! ((invariant))) -> goto accept_all :: (1) -> goto T0_init fi; accept_all: skip } Reader/Writer