53
Class Announcement TA is expected to add/move office hours to tomorrow for Project 0 Project 0 deadline is extended to next Monday April 13 noon. A copy of the text book reserved in the library. Any Issues for Project 0? Pass all public tests locally and then fail in submit.cs?

Class Announcement TA is expected to add/move office hours to tomorrow for Project 0 Project 0 deadline is extended to next Monday April 13 noon. A copy

Embed Size (px)

Citation preview

Class Announcement

• TA is expected to add/move office hours to tomorrow for Project 0

• Project 0 deadline is extended to next Monday April 13 noon.

• A copy of the text book reserved in the library.

• Any Issues for Project 0? Pass all public tests locally and then fail in

submit.cs?

Testfile2 without/with an invalid command

ls -l > tempwc templs -l -f > tempwc < temp > temp2sort < temp > temp3cat temp2diff temp2 temp3 > temp4wc temp4exit

ls -l > tempwc templs -l -f > tempwc < temp > temp2sort < temp > temp3catt temp2diff temp2 temp3 > temp4wc temp4exit

Testfile4 without/with an invalid command

ls -l -f | sort > templs -l | sort > temp1diff temp temp1 > temp2cat < temp | uniq | sort > temp3diff temp3 temp |sort | wc > temp4cat temp4exit

ls -l -f | sort > templs -l | sort > temp1diff temp temp1 > temp2cat < temp | uniq | sort > temp3catt temp3 | wcdiff temp3 temp |sort | wc > temp4cat temp4exit

Autograding only compares with expected answers for processing all valid commands. Donot test weird cases.

Lecture 5: Process/Thread Synchronization

CS170. UCSB.2015. T. Yang

Topics

• The critical-section problem and examples

• Solutions Locks Semaphores Condition variables (monitors).

• Synchronization in Nachos and Pthreads

Application example that needs synchronization2 producer threads and 2 consumer threads

Shared data

Shared variables in a producer/consumer program

count -- number of items available in -- points to the writeable slot out -- points to the slot to read

in out

Producer Thread Code

while (true) {

while (count == BUFFER_SIZE) ;

// buffer is full, spin

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

}

in

Consumer thread

while (true) {

while (count == 0) ; // Nothing is available. Spin

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

}

out

Race Conditions: Example

Count--;

Producer thread Consumer thread

Count++;

Count=5

Is count still 5?

Race Conditions: two or more processes (threads) are reading and writing on shared data and the final result depends on who runs precisely & when

Review compiled code

Count—: register2 = count register2 = register2 - 1 count = register2

Producer thread Consumer thread

Count++: register1 = count register1 = register1 + 1 count = register1

Count=5

Is count still 5?

Executing compiled code

Count—:

register2 = count register2 = register2 - 1 count = register2

Producer thread Consumer thread

Count++: register1 = count register1 = register1 + 1 count=register1

Count=5

Time Is count still 5? 4, 6?Is count still 5? 4, 6?

Executing compiled code

Count—: register2 = count

register2 = register2 - 1

count = register2

Producer thread Consumer thread

Count++: register1 = count

register1 = register1 + 1

count = register1

Count=5

Is count still 5? 4, 6?Is count still 5? 4, 6?Time

Critical-Section Management

Critical section: the part of the program where shared variables are accessed

Property of Critical-Section Solutions

1.Mutual Exclusion – Only one can enter the critical section. Safety property in AD book

2.Progress - If some processes wish to enter their critical section and nobody is in the critical section, then one of them will enter in a limited time. Liveness property in AD book

3.Bounded Waiting - If one process starts to wait for entering an critical section, there is a limit on the number of times other processes entering the section before this process enters. Liveness property in AD book

1.Locks

2.Semaphore

3.Conditional Variables

Solution to Critical-section Problem

Acquire lock

Critical section

Release lock

Lock for Critical-section Problem

•Thread waits if there is a lock.•It enters the critical after acquiring a lock.•Only the thread who locks can unlock.

Nachos Lock Interface

• Lock can be in one of two states: locked or unlocked

• class Lock {

public:

Lock (char *name);

//create a lock with unlocked state

~Lock();

void Acquire(); //Atomically waits until the lock is unlocked, and then sets the lock to be locked.

void Release(); //Atomically changes the state to be unlocked. Only the thread who owns the lock can release.

}

How to use locks

• Typically associate a lock with a piece of shared data for mutual execlusion.

• When a thread needs to access, it first acquires the lock, and then accesses data. Once access completes, it releases the lock.

Example

Lock *k= new Lock (“Lock”);

k->Acquire();

Perform critical section operations.

k->Release();

}

Synchronization with Locks: Example

int a=0;Lock *k;void sum (int p){ int c; k->Acquire();

a=a+1;c=a;k->Release()printf(“%d : a= %d\n”, p,c);

}

void main() {

Thread *t=new Thread (“Child”);

k=new Lock(“Lock”);

t->Fork(sum,1);

sum(0);

}

Semaphore for the critical-section problem

• Semaphore S – nonnegative integer variable• Can only be accessed /modified via two indivisible

(atomic) operations wait (S) { //also called P() while S <= 0

; // wait in a queue S--; } signal (S) { //also called V() //wake up some waiting thread S++; }

Semaphore

• Counting semaphore – initial value representing how many threads can be in the critical section. Binary semaphore (also known as mutex locks) – integer value

ranged between 0 and 1.• Provides mutual exclusion

Semaphore mutex; // initialized to 1

do {

P(mutex);

// Critical Section

V(mutex);

} while (TRUE);• Not recommended by the AD text book (Chapter 5.8), but still

widely used.

Semaphores in Nachos

• class Semaphore{

public:

Semaphore (char *name, int counter);

// initial counter value

~Semaphore();

void P(); //Atomically waits until the counter is greater than 0 and then decreases the counter

void V(); //Atomically increases the counter

}

Synchronization with Semaphore: Example

int a=0;Semaphore *s;void sum (int p){ int c; s->P();

a=a+1;c=a;s->V();printf(“%d : a= %d\n”, p,c);

}

void main() {

Thread *t=new Thread (“Child”);

s=new Semaphore(“S”,1);

t->Fork(sum,1);

sum(0);

}

Synchronization in Consumer thread

while (true) {

while (count == 0) ; // sping waiting

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use data for other things

}

out

Synchronization in Producer/ Consumer threads

Consumer loop:

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use data and do others

Producer loop:

Produce next item

while (count == BUFFER_SIZE) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

out

What is shared?How to synchronize?

count

in

Synchronization AttemptConsumer loop:

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use dataProducer loop:

Produce next item

while (count == BUFFER_SIZE) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

out

incount

Deadlock?How to avoid spin?

Another Synchronization AttemptConsumer loop:

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use dataProducer loop:

Produce next item

while (count == BUFFER_SIZE) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

out

incount

Mutual execution?

in

What can happen with this trace?

Consumer 1 :

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Do other things

out

incount

in

Consumer 2:

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use data and do others

Semaphore for consumer/producer problem

• Simplify the problem: Unbounded buffer Worry about bounded buffer constraint later

• Condition to check: Is there a data item available to consume? Not available? Wait using 1 semaphore

Semaphore for consumer-producer problem with unbounded buffer

Semaphore *data =new Semaphore(“Data”, 0);– Indicate # of data items available for consuming

Consumer thread: while(1) {

data-> P(); //wait until an item is available

Use data item;}

Producer thread: while(1){

produce next item;data->V(); //notify an item is available

}

Semaphore can do more than mutual exclusion, and can synchronize a consumer/producer pipe

Semaphore for consumer/producer problem with bounded buffer

What condition to check for bounded buffer?

Conditions to check: Is there a data item available to consume? Is there space available to produce a data

item? Wait using two semaphores

Consumer-producer problem with bounded buffer

• Two semaphores: Semaphore *data =new Semaphore(“Data”, 0);

– Indicate # of data items available for consuming Semaphore *space =new Semaphore(“Space”, 10); Indicate # of 10 data slots available.

Producer thread:

while(1){

produce next item;

data->V(); //notify item is available

}

Consumer-producer problem with bounded buffer

• Two semaphores: Semaphore *data =new Semaphore(“Data”, 0);

– Indicate # of data items available for consuming Semaphore *space =new Semaphore(“Space”, 10); Indicate # of 10 data slots available.

Producer thread:

while(1){

space->P();//wait until space is available

produce next item;

data->V(); //notify data is available

}

Consumer-producer problem with bounded buffer

Semaphore *data =new Semaphore(“Data”, 0); Semaphore *space =new Semaphore(“Space”, 10); Producer thread:

while(1){

space->P();//wait until space is available

produce next item;

data->V(); //notify data is available

} Consumer thread:

while(1){

data->P();//wait until data is available

consume next item;

}

Consumer-producer problem with bounded buffer

Semaphore *data =new Semaphore(“Data”, 0); Semaphore *space =new Semaphore(“Space”, 10); Producer thread:

while(1){

space->P();//wait until space is available

produce next item;

data->V(); //notify data is available

} Consumer thread:

while(1){

data->P();//wait until data is available

consume next item;

space->V();//notify space is available

}

Synchronization for pthreads: Mutex

pthread_mutex_t mutex; const pthread_mutexattr_t attr;int status;

status = pthread_mutex_init(&mutex,&attr);

status = pthread_mutex_destroy(&mutex);

status = pthread_mutex_unlock(&mutex);

status = pthread_mutex_lock(&mutex); - block

Thread i

……lock(mutex)……critical region……unlock(mutex)……

Semaphore for Pthreads

int status,pshared;

sem_t sem;

unsigned int initial_value;

status = sem_init(&sem,pshared,initial_value);

status = sem_destroy(&sem);

status = sem_post(&sem);

status = sem_wait(&sem);

What now?Consumer loop:

while (count == 0) ;

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

Use dataProducer loop:

Produce next item

while (count == BUFFER_SIZE) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

out

incount

How to use semaphore with lock?

Synchronization with semaphoreConsumer loop:

data->P();

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

space->V();

Use dataProducer loop:

Produce next item

space->P();//wait until space is available) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

data->V(); //notify data is available

Synchronization with semaphore/lock?Consumer loop:

data->P();

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

space->V();

Use dataProducer loop:

Produce next item

space->P();//wait until space is available) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

data->V(); //notify data is available

What happens when a consumer does not hod a lock after waking up from data->p()?

Consumer 1:

data->P();

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

space->V();

Use data

Consumer 2:

data->P();

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

space->V();

Use data

Synchronization with semaphore/lock?Consumer loop:

data->P();

nextConsumed = buffer[out];

out = (out + 1) % BUFFER_SIZE;

count--;

space->V();

Use dataProducer loop:

Produce next item

space->P();//wait until space is available) ;

buffer [in] = nextProduced;

in = (in + 1) % BUFFER_SIZE;

count++;

data->V(); //notify data is available

How to hold a lock immediately after waking up?

Solution to Critical-section Problem

1.Locks

2.Semaphore

3.Conditional Variables

Condition Variables in Nachos• Pair it with a lock

• class Condition {

public:

Condition(char *name);

~Condition();

void Wait(Lock *mylock); //Atomically releases the lock and waits. When it is returned, the lock is reacquired again.

void Signal(Lock *myLock); //Wake up one waiting thread to run. The lock is not released.

void Broadcast(Lock *myLock); //Wake up all threads waiting on the condition. The lock is not released.

}

Condition Variables for consumer-producer problem with unbounded buffer

• int avail=0; // # of data items available for consumption• Lock *L; Condition *cv= new Condition (“condition”);• Consumer thread loop:

L.Acquire();if (avail <=0) cv.Wait(L);

Fetch next item; avail = avail-1;L.Release();Do other things

Producer thread loop: L.Acquire();Add next item; avail = avail+1; cv.Signal(L); //notify an item is availableL.Release();Do other things

Can conditionstill be true afterwake-up?How to protect?

Condition Variables for consumer-producer problem with unbounded buffer

• int avail=0; // # of data items available for consumption• Lock *L; Condition *cv= new Condition (“condition”);• Consumer thread loop:

L.Acquire();while(avail <=0) cv.Wait(L);

Fetch next item; avail = avail-1;L.Release();Do other things

Producer thread loop: L.Acquire();Add next item; avail = avail+1; cv.Signal(L); //notify an item is availableL.Release();Do other things

Thread 1

mylock.Acquire();

When condition is not satisfied,

cv.Wait(mylock);

Critical Section;

mylock.Release(); Thread 2:

mylock.Acquire();

When condition can satisfy,

cv.Signal(mylock);

mylock.Relase();

How to Use Condition Variables: Typical Flow

When to use condition broadcast()?

• When waking up one thread to run is not sufficient.

• Example: concurrent malloc()/free() for allocation and deallocation of objects with non-uniform sizes.

malloc()/free() with condition broadcast• Initially 10 bytes are free. • m() stands for malloc(). f() for free()

Thread 1:

m(10) – succ

f(10) –broadcast?

m(7) – wait

Resume m(7)-wait

Thread 2:

m(5) – wait

Resume m(5)-succ

f(5) –broadcast?

Thread 3:

m(5) – wait

Resume m(5)-succ

m(3) –wait

Resume m(3)-succ

malloc()/free() with condition signal• Initially 10 bytes are free. • m() stands for malloc(). f() for free()

Thread 1:

m(10) – succ

f(10) –signal?

m(7) – wait

Resume m(7)-wait

Thread 2:

m(5) – wait

Resume m(5)-succ

f(5) –signal?

Thread 3:

m(5) – wait

Resume m(5)-succ

m(3) –wait

Resume m(3)-succ

Pthread synchronization: Condition variables

int status;

pthread_condition_t cond;

const pthread_condattr_t attr;

pthread_mutex mutex;

status = pthread_cond_init(&cond,&attr);

status = pthread_cond_destroy(&cond);

status = pthread_cond_wait(&cond,&mutex);

status = pthread_cond_signal(&cond);

status = pthread_cond_broadcast(&cond);

Summary

• Concurrent threads are a very useful abstraction Allow transparent overlapping of computation and I/O Allow use of parallel processing when available

• Concurrent threads introduce problems when accessing shared data Programs must be insensitive to arbitrary interleaving Without careful design, shared variables can become

completely inconsistent• Synchronization with lock, condition variables, &

semaphores