Concurrency Concurrency A process is a program executing

  • Slides: 38
Download presentation
Concurrency

Concurrency

Concurrency • A process is a program executing on a virtual computer • Processor

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

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

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

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

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

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.

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.

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

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. •

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

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(“Producen”) turn

Producer Active proctype producer () { do : : (turn== P) -> printf(“Producen”) turn = C od }

Consumer Active proctype consumer() { do : : (turn== C) -> printf(“t. Consumern”) turn

Consumer Active proctype consumer() { do : : (turn== C) -> printf(“t. Consumern”) turn = P od }

Correctness Conditions • A correct solution to the critical section problem must satisfy: –

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

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 • Not using UNIX: Spin –u 14 prodcons. pml Produce Consume --------Depth-limit (-u 14 steps) reached

Extensing the Example • Instantiate more than 1 process Active [2] proctype producer() {…}

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;

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)

Main Code Active [2] proctype producer() { do : : request (turn, P, N) -> printf (“P%dn”, _pid); assert (who== _pid); release (turn, C) od } Active [2] proctype consumer() { do : : request (turn, C, N) -> printf (“C%dn”, _pid); assert (who== _pid); release (turn, P) od }

Printout Spin prodcons 2. pml P 1 C 2 P 1 C 3 P

Printout Spin prodcons 2. pml P 1 C 2 P 1 C 3 P 0 P 1 C 3 … • 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“,

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 prodcons 2. pml #generate a verifier cc –o pan. c #compile the verifier

. /pan #perform the verification (Spin version 4. 0. 7 - - 1 August

. /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

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

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. c. /pan (Spin Version 4.

Spin Verification spin -a mutex. pml cc -o 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() {

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; 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. c. pan: assertion violated

Spin’s Verdict spin – a mutex_flaw. pml cc -0 pan. c. pan: assertion violated (cnt==1) (at depth 53) pan: wrote mutex_flaw. pml. trail

Bakery Algorithm proctype A() { do : : 1 -> turn. A = 1;

Bakery Algorithm proctype A() { do : : 1 -> turn. A = 1; turn. A = turn. B + 1; (turn. B == 0) || (turn. A < turn. B); mutex ++; mutex --; turn. A = 0; od }

Dekker’s solution to the two process mutual exclusion problem #define true 1 #define false

Dekker’s solution to the two process mutual 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

Peterson's Mutual Exclusion bit active [2] ; bit last ; byte crit ; proctype thread. MX (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 thread. MX (0) ; run thread. MX (1) ; }

Message Passing Mtype = {ini, ack, dreg, data, shutup, quiet, dead}; Chan M =

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; M? ack; } /*connection */ /*handshake */ timeout-> if

Active proctype Mproc() { W!ini; M? ack; } /*connection */ /*handshake */ timeout-> if : : W!shutup : : W!dreq M? data do : : W!data : : W!shutup; break; od fi; /*wait */ /*two options */ /*start shutdown */ /*or request data */ /*receive data */ M? shutup; W!quiet; M? dead /*shutdown handshake */ /*send data */ /*or shutdown */

Active proctype Wproc() { W? ini; /*wait for ini M!ack; /*acknowledge do : :

Active proctype Wproc() { W? ini; /*wait for ini M!ack; /*acknowledge do : : W? dreq -> M!data */ : : W? data -> skip : : W? shutup -> M!shutup; break; od; W? quiet; M!dead } */ */ /*3 options */ /*data requested */ /*send data /*receive data /*no response /*start shutdown */ */ */

Spin –c protocol Proc 0 = Mproc Proc 1 = Wproc qp 0 1

Spin –c protocol Proc 0 = Mproc Proc 1 = Wproc qp 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 2 2 M? dead … W? quiet M!dead

SPIN Output proctype A state 1 -> state 2 => x = 1 state

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(){

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;

Reader/Writer #define invariant (nr == 0 || !busy) byte nr; bool busy; proctype reader(){

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; init{ nr=0; busy=0; run reader(); run writer(); } if : : (! ((invariant))) -> goto accept_all : : (1) -> goto T 0_init fi; accept_all: skip }