cp — concurrent programming 3. safety and synchronization prof. o. nierstrasz wintersemester 2005...
Post on 21-Dec-2015
217 views
TRANSCRIPT
CP — Concurrent Programming
3. Safety and Synchronization
Prof. O. NierstraszWintersemester 2005 / 2006
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.2
Safety and Synchronization
Overview> Modelling interaction in FSP> Safety — synchronizing critical sections
— Locking for atomicity— The busy-wait mutual exclusion protocol
> Conditional synchronization— Slots in FSP— wait(), notify() and notifyAll()— Slots in Java
Selected material © Magee and Kramer
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.3
Modelling interaction — shared actions
Actions that are common between two processes are shared and can be used to model process interaction:
> Unshared actions may be arbitrarily interleaved> Shared actions occur simultaneously for all participants
What are the states of the LTS? The traces?
What are the states of the LTS? The traces?
MAKER = ( make -> ready -> MAKER ).USER = ( ready -> use -> USER ).
||MAKER_USER = ( MAKER || USER ).
MAKER = ( make -> ready -> MAKER ).USER = ( ready -> use -> USER ).
||MAKER_USER = ( MAKER || USER ).
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.4
Modelling interaction — handshake
A handshake is an action that signals acknowledgement
What are the states and traces of the LTS?
What are the states and traces of the LTS?
0 1
MAKERv2 = ( make -> ready -> used -> MAKERv2 ).USERv2 = ( ready -> use -> used -> USERv2 ).
||MAKER_USERv2 = ( MAKERv2 || USERv2 ).
MAKERv2 = ( make -> ready -> used -> MAKERv2 ).USERv2 = ( ready -> use -> used -> USERv2 ).
||MAKER_USERv2 = ( MAKERv2 || USERv2 ).
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.5
Safety problems
Objects must only be accessed when they are in a consistent state, formalized by a class invariant.
Each method assumes the class invariant holds when it starts, and it re-establishes it when done.
If methods interleave arbitrarily, an inconsistent state may be accessed, and the object may be left in a “dirty” state. Where shared resources are
updated may be a critical section.Where shared resources are
updated may be a critical section.
m1
m2
m3
m4
m5
Consistent states
?!?!
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.6
Atomicity and interference
Consider the two processes:
How can these processes interfere?
How can these processes interfere?
{ x = 0 }AInc: x := x+1BInc: x := x+1
{ x = ? }
{ x = 0 }AInc: x := x+1BInc: x := x+1
{ x = ? }
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.7
Atomic actions
Individual reads and writes may be atomic actions:
const N = 3range T = 0..N
Var = Var[0],Var[u:T] = ( read[u] -> Var[u]
| write[v:T] -> Var[v] ).
set VarAlpha = { read[T], write[T] }Inc = ( read[v:0..N-1]
-> write[v+1]-> STOP ) +VarAlpha.
const N = 3range T = 0..N
Var = Var[0],Var[u:T] = ( read[u] -> Var[u]
| write[v:T] -> Var[v] ).
set VarAlpha = { read[T], write[T] }Inc = ( read[v:0..N-1]
-> write[v+1]-> STOP ) +VarAlpha.
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.8
Sequential behaviour
A single sequential thread requires no synchronization:
Var
{read, write}[0]
write[1]
write[2]
write[3]
write[0]
{read, write}[1]
write[2]
write[3]
write[0]
write[1]
{read, write}[2]
write[3]
write[0]
write[1]
write[2]
{read, write}[3]0 1 2 3
Incread[0]
read[1]
read[2]
write[1]
write[2]
write[3]
0 1 2 3 4
SeqInc read[0] write[1]
0 1 2||SeqInc = (Var||Inc).||SeqInc = (Var||Inc).
0 1
0 0 0 1
1 0
2 0
0 2 0 3 0 4
read[1]read[2]
3 0
1 1
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
1 4
2 4
3 4
read[0]write[0]
write[2]
write[3]
Var
Inc
write[1] write[1]
write[2]
write[3]
read[1]write[1]
write[0]
read[0]
read[0] write[1]
write[1]
write[2]
write[3]
read[0] / write[0]
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.10
ParInc
a.read[0]
b.read[0]
a.read[0]
b.write[1]a.read[1]a.write[2]
a.write[1]
b.write[1]a.write[1]
b.write[1]
a.write[1]
b.read[0]
b.read[1]
b.write[2]
0 1 2 3 4 5 6 7 8 9 10 11
Concurrent behaviour
Without synchronization, concurrent threads may interfere:
||ParInc = ({a,b}::Var || a:Inc || b:Inc).||ParInc = ({a,b}::Var || a:Inc || b:Inc).0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.11
Locking
Locks are used to make a critical section atomic:
INCacquire read[0]
read[1]
read[2]
write[1] release write[2]
release
write[3]
release
0 1 2 3 4 5 6 7 8
LOCK = ( acquire -> release -> LOCK ).INC = ( acquire
-> read[v:0..N-1]-> write[v+1]-> release-> STOP ) +VarAlpha.
LOCK = ( acquire -> release -> LOCK ).INC = ( acquire
-> read[v:0..N-1]-> write[v+1]-> release-> STOP ) +VarAlpha.
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.12
Synchronization
Processes can synchronize critical sections by sharing a lock:
ParInc2
a.acquire
b.acquireb.read[0]b.write[1]b.releasea.acquirea.read[1]a.write[2]a.release a.read[0]a.write[1]a.releaseb.acquireb.read[1]b.write[2]
b.release
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
||ParInc2 = ({a,b}::VAR || {a,b}::LOCK || a:INC || b:INC).||ParInc2 = ({a,b}::VAR || {a,b}::LOCK || a:INC || b:INC).
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.13
Synchronization in Java
Java Threads also synchronize using locks:
is just convenient syntax for:
Every object has a lock, and Threads may use them to synchronize with each other.
synchronized T m() {// method body
}
synchronized T m() {// method body
}
T m() {synchronized (this) {
// method body}
}
T m() {synchronized (this) {
// method body}
}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.14
Busy-Wait Mutual Exclusion Protocol
P1 sets enter1 := true when it wants to enter its CS, but sets turn := “P2” to yield priority to P2
Is this protocol correct? Is it fair? Deadlock-free?Is this protocol correct? Is it fair? Deadlock-free?
process P1loop
enter1 := true turn := “P2”while enter2 and
turn = “P2”do skip
Critical Sectionenter1 := falseNon-critical Section
endend
process P1loop
enter1 := true turn := “P2”while enter2 and
turn = “P2”do skip
Critical Sectionenter1 := falseNon-critical Section
endend
process P2loop
enter2 := trueturn := “P1”while enter1 and
turn = “P1”do skip
Critical Sectionenter2 := falseNon-critical Section
endend
process P2loop
enter2 := trueturn := “P1”while enter1 and
turn = “P1”do skip
Critical Sectionenter2 := falseNon-critical Section
endend
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.15
Atomic read and write
We can model integer and boolean variables as processes with atomic read and write actions:
range T = 1..2
Var = Var[1],Var[u:T] =
( read[u] -> Var[u]| write[v:T] -> Var[v]).
set Bool = {true,false}
BOOL(Init='false) = BOOL[Init],BOOL[b:Bool] =
( is[b] -> BOOL[b]| setTo[x:Bool] -> BOOL[x]).
range T = 1..2
Var = Var[1],Var[u:T] =
( read[u] -> Var[u]| write[v:T] -> Var[v]).
set Bool = {true,false}
BOOL(Init='false) = BOOL[Init],BOOL[b:Bool] =
( is[b] -> BOOL[b]| setTo[x:Bool] -> BOOL[x]).
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.16
Modelling the busy-wait protocol
Each process performs two actions in its CS:
P1 = ( enter1.setTo['true]-> turn.write[2]-> Gd1),
Gd1 =( enter2.is['false] -> CS1| enter2.is['true] ->
( turn.read[1] -> CS1| turn.read[2] -> Gd1)),
CS1 = ( a -> b-> enter1.setTo['false]-> P1).
P1 = ( enter1.setTo['true]-> turn.write[2]-> Gd1),
Gd1 =( enter2.is['false] -> CS1| enter2.is['true] ->
( turn.read[1] -> CS1| turn.read[2] -> Gd1)),
CS1 = ( a -> b-> enter1.setTo['false]-> P1).
P2 = ( enter2.setTo['true]-> turn.write[1]-> Gd2),
Gd2 =( enter1.is['false] -> CS2| enter1.is['true] ->
( turn.read[2] -> CS2| turn.read[1] -> Gd2)),
CS2 = ( c -> d-> enter2.setTo['false]-> P2).
P2 = ( enter2.setTo['true]-> turn.write[1]-> Gd2),
Gd2 =( enter1.is['false] -> CS2| enter1.is['true] ->
( turn.read[2] -> CS2| turn.read[1] -> Gd2)),
CS2 = ( c -> d-> enter2.setTo['false]-> P2).
||BusyWait = (enter1:BOOL||enter2:BOOL||turn:Var||P1||P2)@{a,b,c,d}.||BusyWait = (enter1:BOOL||enter2:BOOL||turn:Var||P1||P2)@{a,b,c,d}.
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.17
BusyWait
tau
tau
tau
tau
tau
tau
c
tau
d
tau
tau
tau
tau
a
tau
b
tau
c tau
d
d
tau
a tau
b
b
a
c
tau
tau tau
b
tau
a
tau
d
tau
c
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Busy-wait composition
Very pretty, but how do we know there are
no errors?!
Very pretty, but how do we know there are
no errors?!
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.18
Checking for errors
We can check for errors by composing our system with an agent that moves to the ERROR state if atomicity is violated:
What happens if we break the protocol?What happens if we break the protocol?
Ok = ( a -> ( c -> ERROR | b -> Ok )| c -> ( a -> ERROR | d -> Ok)).
||BusyWaitOK = (enter1:BOOL||enter2:BOOL||turn:Var||P1||P2||Ok).
Ok = ( a -> ( c -> ERROR | b -> Ok )| c -> ( a -> ERROR | d -> Ok)).
||BusyWaitOK = (enter1:BOOL||enter2:BOOL||turn:Var||P1||P2||Ok).
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.19
Conditional synchronization
A lock delays an acquire request if it is already locked:
Similarly, a one-slot buffer delays a put request if it is full and delays a get request if it is empty:
Slot
put[0]
put[1]
put[2]
get[0]
get[1]
get[2]
0 1 2 3
LOCK =( acquire -> release -> LOCK ).LOCK =( acquire -> release -> LOCK ).
const N = 2Slot = ( put[v:0..N]
-> get[v] -> Slot ).
const N = 2Slot = ( put[v:0..N]
-> get[v] -> Slot ).
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.20
Producer/Consumer composition
Producer put[0] put[1]
put[2]
0 1 2
Consumer
get[0..2]0
Chain put[0] get[0] put[1] get[1] put[2]
get[2]
0 1 2 3 4 5
Producer = ( put[0]-> put[1] -> put[2]-> Producer ).
Consumer = ( get[x:0..N]-> Consumer ).
||Chain = ( Producer|| Slot|| Consumer )
Producer = ( put[0]-> put[1] -> put[2]-> Producer ).
Consumer = ( get[x:0..N]-> Consumer ).
||Chain = ( Producer|| Slot|| Consumer )
0 1
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.21
Wait and notify
A Java object whose methods are all synchronized behaves like a monitor
Within a synchronized method or block:> wait() suspends the current thread, releasing the lock> notify() wakes up one thread waiting on that object> notifyAll() wakes up all threads waiting on that object
Outside of a synchronized block, wait() and notify() will raise an IllegalMonitorStateException
Always use notifyAll() unless you are sure it doesn’t matter which thread you wake up!
Always use notifyAll() unless you are sure it doesn’t matter which thread you wake up!
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.22
Slot (put)
class Slot implements Buffer {private Object slotVal; // initially nullpublic synchronized void put(Object val) {
while (slotVal != null) {try { wait(); } // become NotRunnablecatch (InterruptedException e) { }
}slotVal = val;notifyAll(); // make waiting threads Runnable return;
}…
class Slot implements Buffer {private Object slotVal; // initially nullpublic synchronized void put(Object val) {
while (slotVal != null) {try { wait(); } // become NotRunnablecatch (InterruptedException e) { }
}slotVal = val;notifyAll(); // make waiting threads Runnable return;
}…
interface Buffer {public void put(Object val);public Object get();
}
interface Buffer {public void put(Object val);public Object get();
}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.23
Slot (get)
…public synchronized Object get() {
Object rval;while (slotVal == null) {
try { wait(); }catch (InterruptedException e) { }
}rval = slotVal;slotVal = null;notifyAll();return rval;
}}
…public synchronized Object get() {
Object rval;while (slotVal == null) {
try { wait(); }catch (InterruptedException e) { }
}rval = slotVal;slotVal = null;notifyAll();return rval;
}}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.24
Active objects
abstract class ActiveObject extends Thread {protected int count_;ActiveObject(String name, int count) {
super(name);count_ = count;
}public void run() {
int i;for (i=1;i<=count_;i++) {
this.action(i);this. randomSleep();
}}protected abstract void action(int n);protected void randomSleep() { … }
}
abstract class ActiveObject extends Thread {protected int count_;ActiveObject(String name, int count) {
super(name);count_ = count;
}public void run() {
int i;for (i=1;i<=count_;i++) {
this.action(i);this. randomSleep();
}}protected abstract void action(int n);protected void randomSleep() { … }
}
An active object has a thread of its own.
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.25
Producer in Java
The Producer puts _count messages to the slot:
class Producer extends ActiveObject {protected Buffer slot_;protected String wares_;Producer(String name, int count, Buffer slot, String wares) {
super(name, count);slot_ = slot;wares_ = wares;
}protected void action(int n) {
String message;message = wares_ + "(" + String.valueOf(n) + ")";slot_.put(message);System.out.println(getName() + " put " + message);
}}
class Producer extends ActiveObject {protected Buffer slot_;protected String wares_;Producer(String name, int count, Buffer slot, String wares) {
super(name, count);slot_ = slot;wares_ = wares;
}protected void action(int n) {
String message;message = wares_ + "(" + String.valueOf(n) + ")";slot_.put(message);System.out.println(getName() + " put " + message);
}}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.26
Consumer in Java
... and the Consumer gets them:
class Consumer extends ActiveObject {protected Buffer slot_;Consumer(String name, int count, Buffer slot) {
super(name, count);slot_ = slot;
}protected void action(int n) {
String message;message = (String) slot_.get();System.out.println(getName() + " got " + message);
}}
class Consumer extends ActiveObject {protected Buffer slot_;Consumer(String name, int count, Buffer slot) {
super(name, count);slot_ = slot;
}protected void action(int n) {
String message;message = (String) slot_.get();System.out.println(getName() + " got " + message);
}}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.27
Composing Producers and Consumers
Multiple producers and consumers may share the buffer:Peter put apple (1)Carla got apple (1)Paula put orange(1)Cris got orange(1)Patricia put banana(1)Carla got banana(1)Peter put apple (2)Cris got apple (2)Patricia put banana(2)Carla got banana(2)Peter put apple (3)Cris got apple (3)Paula put orange(2)Carla got orange(2)Patricia put banana(3)Cris got banana(3)Peter put apple (4)Cris got apple (4)Peter put apple (5)Carla got apple (5)Paula put orange(3)Cris got orange(3)Patricia put banana(4)Cris got banana(4)Patricia put banana(5)Cris got banana(5)Paula put orange(4)Cris got orange(4)Paula put orange(5)Cris got orange(5)
Peter put apple (1)Carla got apple (1)Paula put orange(1)Cris got orange(1)Patricia put banana(1)Carla got banana(1)Peter put apple (2)Cris got apple (2)Patricia put banana(2)Carla got banana(2)Peter put apple (3)Cris got apple (3)Paula put orange(2)Carla got orange(2)Patricia put banana(3)Cris got banana(3)Peter put apple (4)Cris got apple (4)Peter put apple (5)Carla got apple (5)Paula put orange(3)Cris got orange(3)Patricia put banana(4)Cris got banana(4)Patricia put banana(5)Cris got banana(5)Paula put orange(4)Cris got orange(4)Paula put orange(5)Cris got orange(5)
public static void ProducerConsumerDemo() {Buffer slot = new Slot();new Producer("Peter", "apple ", slot, count).start();new Producer("Paula", "orange", slot, count).start();new Producer("Patricia", "banana", slot, count).start();
new Consumer("Carla", slot, count).start();new Consumer("Cris", slot, 2*count).start();
}
public static void ProducerConsumerDemo() {Buffer slot = new Slot();new Producer("Peter", "apple ", slot, count).start();new Producer("Paula", "orange", slot, count).start();new Producer("Patricia", "banana", slot, count).start();
new Consumer("Carla", slot, count).start();new Consumer("Cris", slot, 2*count).start();
}
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.28
What you should know!
> How do you model interaction with FSP?> What is a critical section? What is critical about it?> Why don’t sequential programs need synchronization?> How do locks address safety problems?> What primitives do you need to implement the busy-wait
mutex protocol?> How can you use FSP to check for safety violations?> What happens if you call wait or notify outside a
synchronized method or block?> When is it safe to use notifyAll()?
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.29
Can you answer these questions?
> What is an example of an invariant that might be violated by interfering, concurrent threads?
> What constitute atomic actions in Java?> Can you ensure safety in concurrent programs without
using locks?> When should you use synchronize(this) rather than
synchronize(someObject)?> Is the busy-wait mutex protocol fair? Deadlock-free?> How would you implement a Lock class in Java?> Why is the Java Slot class so much more complex than
the FSP Slot specification?
© Oscar Nierstrasz
CP — Safety and Synchronization
CP 3.30
License
> http://creativecommons.org/licenses/by-sa/2.5/
Attribution-ShareAlike 2.5You are free:• to copy, distribute, display, and perform the work• to make derivative works• to make commercial use of the work
Under the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor.
Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
• For any reuse or distribution, you must make clear to others the license terms of this work.• Any of these conditions can be waived if you get permission from the copyright holder.
Your fair use and other rights are in no way affected by the above.
Attribution-ShareAlike 2.5You are free:• to copy, distribute, display, and perform the work• to make derivative works• to make commercial use of the work
Under the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor.
Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.
• For any reuse or distribution, you must make clear to others the license terms of this work.• Any of these conditions can be waived if you get permission from the copyright holder.
Your fair use and other rights are in no way affected by the above.