30
CP — Concurrent Programming 3. Safety and Synchronization Prof. O. Nierstrasz Wintersemester 2005 / 2006

CP — Concurrent Programming 3. Safety and Synchronization Prof. O. Nierstrasz Wintersemester 2005 / 2006

  • View
    217

  • Download
    1

Embed Size (px)

Citation preview

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.