530 likes | 549 Views
The TA will shift office hours to tomorrow. Project 0 deadline is extended to next Monday, April 13, at noon. A copy of the textbook is reserved in the library. Any issues with Project 0? Lecture on Process/Thread Synchronization. Topics: Critical-section problem, locks, semaphores, condition variables, synchronization in Nachos and Pthreads. Managing race conditions, critical-section properties, and solutions. Locks, semaphores, and conditional variables in critical-section problems. Example code and usage of locks for mutual exclusion and synchronization.
E N D
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 > temp wc temp ls -l -f > temp wc < temp > temp2 sort < temp > temp3 cat temp2 diff temp2 temp3 > temp4 wc temp4 exit ls -l > temp wc temp ls -l -f > temp wc < temp > temp2 sort < temp > temp3 catt temp2 diff temp2 temp3 > temp4 wc temp4 exit
Testfile4 without/with an invalid command ls -l -f | sort > temp ls -l | sort > temp1 diff temp temp1 > temp2 cat < temp | uniq | sort > temp3 diff temp3 temp |sort | wc > temp4 cat temp4 exit ls -l -f | sort > temp ls -l | sort > temp1 diff temp temp1 > temp2 cat < temp | uniq | sort > temp3 catt temp3 | wc diff temp3 temp |sort | wc > temp4 cat temp4 exit 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 synchronization 2 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 out in
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=5 Consumer thread Producer thread Count++; Count--; 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=5 Consumer thread Producer thread Count++: register1 = count register1 = register1 + 1 count = register1 Count—: register2 = count register2 = register2 - 1 count = register2 Is count still 5?
Executing compiled code Count=5 Consumer thread Producer thread Count++: register1 = count register1 = register1 + 1 count=register1 Count—: register2 = count register2 = register2 - 1 count = register2 Is count still 5? 4, 6? Time
Executing compiled code Count=5 Consumer thread Producer thread Count++: register1 = count register1 = register1 + 1 count = register1 Count—: register2 = count register2 = register2 - 1 count = register2 Time Is count still 5? 4, 6?
Critical-Section Management Critical section: the part of the program where shared variables are accessed
Property of Critical-Section Solutions • Mutual Exclusion – Only one can enter the critical section. Safety property in AD book • 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 • 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
Solution to Critical-section Problem Locks Semaphore Conditional Variables
Lock for Critical-section Problem Acquire lock Critical section Release lock • 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 What is shared? How to synchronize? 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 in count
Synchronization Attempt Deadlock? How to avoid spin? Consumer loop: while (count == 0) ; nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; Use data Producer loop: Produce next item while (count == BUFFER_SIZE) ; buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; out in count
Another Synchronization Attempt Mutual execution? Consumer loop: while (count == 0) ; nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; Use data Producer loop: Produce next item while (count == BUFFER_SIZE) ; buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; out in in count
What can happen with this trace? Consumer 2: while (count == 0) ; nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; Use data and do others Consumer 1 : while (count == 0) ; nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; Do other things out in in count
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 can do more than mutual exclusion, and can synchronize a consumer/producer pipe • 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 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? How to use semaphore with lock? Consumer loop: while (count == 0) ; nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; Use data Producer loop: Produce next item while (count == BUFFER_SIZE) ; buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; out in count
Synchronization with semaphore Consumer loop: data->P(); nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; space->V(); Use data Producer 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 data Producer 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 2: data->P(); nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; space->V(); Use data Consumer 1: 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 data Producer 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 Locks Semaphore 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 available L.Release(); Do other things Can condition still be true after wake-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 available L.Release(); Do other things
How to Use Condition Variables: Typical Flow • 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();
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 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 Thread 1: m(10) – succ f(10) –broadcast? m(7) – wait Resume m(7)-wait