330 likes | 466 Views
Concurrency: Mutual Exclusion and Synchronization. Chapter 5. Critical Sections & Mutual Exclusion. shared double balance ; Code for p 1 Code for p 2 . . . . . . balance = balance + amount; balance = balance - amount; . . . . . . balance += amount. balance -= amount. Shared.
E N D
Critical Sections & Mutual Exclusion shared double balance; Code for p1Code for p2 . . . . . . balance = balance + amount; balance = balance - amount; . . . . . . balance += amount balance -= amount Shared balance Assembly code for p1 Assembly code for p2 load R1, balance load R1, balance load R2, amount load R2, amount add R1, R2 sub R1, R2 store R1, balance store R1, balance
Critical Sections (cont.) • Mutual exclusionOnly one process can be in the critical section at a time Without mutual exclusion, results of multiple execution are notconsistent • There is a race to execute critical sections • The sections may be defined by different code in different processes. Therefore, it is not easy for compilers to detect them Need an OS mechanism so programmer can resolve races Some Possible OS Mechanisms • Disable interrupts • Software solution – locks, semaphores
Disabling Interrupts shared double balance; Code for p1Code for p2 disableInterrupts(); disableInterrupts(); balance = balance + amount; balance = balance - amount; enableInterrupts(); enableInterrupts(); • Disabling interrupts guarantees mutual exclusion, but … Disadvantages • A user process can easily abuse this privilege and hence should not be available to a user process. • Interrupts could be disabled arbitrarily long • We only want to prevent p1 and p2 from interfering with one another; this prevents any other process pk to execute • In a Multiprocessor system, disabling interrupts in one processor will not disable it in another process and hence mutual exclusion is not guaranteed
Using a lock Variable shared booleanlock = FALSE; shared double balance; Code for p1Code for p2 /* Acquire the lock *//* Acquire the lock */ while (lock) /*loop */ ; while (lock) /*loop */ ; lock = TRUE; lock = TRUE; /* Execute critical sect *//* Execute critical sect */ balance = balance + amount; balance = balance - amount; /* Release lock *//* Release lock */ lock = FALSE; lock = FALSE; • Access to lock is not atomic, race condition is still there! • Is it possible to solve the problem?
New Solution shared booleanlock = FALSE; shared list L; Code for p1Code for p2 . . . . . . get(lock);get(lock); <execute critical section>;<execute critical section>; release(lock);release(lock); . . . . . . However, get(lock) and release(lock) operations must be atomic !
Important considerations for software locks • Mutual exclusion: Only one process at a time in the Critical Section (CS) • Only the processes competing for a CS must be considered for resolving who enters the CS next. • Once a process attempts to enter its CS, it should not be postponed indefinitely NO STARVATION! (After requesting entry, only a bounded number of other processes may enter before the requesting process) • No deadlock or starvation should occur • A process should not be delayed access to a critical section when there is no other process using it • No assumptions should be made about the relative speeds of processes or the number of competing processes
Dijkstra’s Semaphore • Invented in the 1960s • Conceptual OS mechanism, with no specific implementation defined (could be get()/release()) • Basis of all contemporary OS synch. mechanisms • A semaphore, s, is a nonnegative integer variable that can only be changed or tested by these two atomic (indivisible / uninterruptable) functions: P(s) : [ while(s == 0) {wait}; s = s – 1; ] V(s) : [ s = s + 1;]
Semaphore solution structsemaphore { intcount; queueTypequeue; } void get(semaphore s) disable_interrupts(); { s.count--; if (s.count < 0) {place this process in s.queue; block this process } enable_interrupts(); } void release(semaphore s) {disable_interrupts(); s.count++; if (s.count <= 0) { remove a process P from s.queue; place process P on ready queue; } enable_interrupts(); } get( ) P( ) wait( ) pthread_mutex_lock() release( ) V( ) signal( ) pthread_mutex_unlock()
Shared Account Problem • P0() • { • . . . • /* Enter the CS */ • P(mutex); • balance += amount; • V(mutex); • . . . • } • P1() • { • . . . • /* Enter the CS */ • P(mutex); • balance -= amount; • V(mutex); • . . . • } semaphore mutex = 1; pthread_create(P0, 0); pthread_create(P1, 0);
Semaphore/Critical Section example A is done! What is going to happen now?
Processing Two Critical Sections shared lock1 = FALSE; shared lock2 = FALSE; Code for p1 . . . /* Enter CS-1 */ get(lock1); <critical section 1>; release(lock1); <other computation>; /* Enter CS-2 */ get(lock2); <critical section 2>; release(lock2); . . . shared lock1 = FALSE; shared lock2 = FALSE; Code for p2 . . . /* Enter CS-2*/ get(lock2); <critical section 2>; release(lock2); <other computation>; /* Enter CS-1 */ get(lock1); <critical section 1>; release(lock1); . . .
Deadlockmay occur if locks are not used properly! shared boolean lock1 = FALSE; shared boolean lock2 = FALSE; Code for p1Code for p2 . . . . . . get(lock1); get(lock2); <delete element>; <update length>; /* Enter CS to update length */ /* Enter CS to add element */ get(lock2); get(lock1); <update length>; <add element>; release(lock2); releaselock1); release(lock1); release(lock2); . . . . . .
OS concerns related to concurrency • Synchronization (support for mutual exclusion) • Deadlock • Resouce Allocation / deallocation • Processor • Memory • Files • I/O devices • Protection of data/resources (access control for sharing)
Producer/Consumer Problem • One or more producers are generating data and placing these in a buffer • A single consumer is taking items out of the buffer one at time Producer: while (true) { /* produce item v */ b[in] = v; in++; } Consumer: while (true) { while (in <= out) /* wait */; w = b[out]; out++; /* consume item w */ } Infinite Buffer Since Buffer b[], “in” and “out” pointers are all shared, these solutions do not work! Only one producer or consumer should access the buffer at any one time !
Producer/Consumer using a Circular Buffer Consumer: while (true) { while (in == out) /* do nothing */; w = b[out]; out = (out + 1) % n; /* consume item w */ } Producer: while (true) { /* produce item v */ while((in + 1)%n == out) /* do nothing */; b[in] = v; in = (in + 1) % n } Since Buffer b[], “in” and “out” pointers are all shared, these solutions do not work, either!
A Solution to Bounded-Buffer Producer/Consumer Problem /* lock for Full Pool */ /* of items in Full Pool */ Bounded Buffer Empty Pool Producer Consumer Producer Consumer Producer Full Pool
Readers-Writers Problem Writer Reader Writer Reader Writer Reader Writer Reader Writer Reader Writer Reader Writer Reader Reader Shared Resource
Readers-Writers Problem Writer Writer Writer Writer Writer Writer Writer Reader Reader Reader Reader Reader Reader Reader Reader Shared Resource
Readers-Writers Problem Reader Writer Reader Writer Reader Writer Reader Writer Reader Writer Reader Writer Reader Reader Writer Shared Resource
First Solution reader() { while(TRUE) { <other computing>; P(mutex); readCount++; if(readCount == 1) P(writeBlock); V(mutex); /* Critical section */ access(resource); P(mutex); readCount--; if(readCount == 0) V(writeBlock); V(mutex); } } resourceType *resource; intreadCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; writer() { while(TRUE) { <other computing>; P(writeBlock); /* Critical section */ access(resource); V(writeBlock); } } • First reader competes with writers • Last reader signals writers
2 4 5 3 1 First Solution reader() { while(TRUE) { <other computing>; P(mutex); readCount++; if(readCount == 1) P(writeBlock); V(mutex); /* Critical section */ access(resource); P(mutex); readCount--; if(readCount == 0) V(writeBlock); V(mutex); } } resourceType *resource; intreadCount = 0; semaphore mutex = 1; semaphore writeBlock = 1; writer() { while(TRUE) { <other computing>; P(writeBlock); /* Critical section */ access(resource); V(writeBlock); } } • First reader competes with writers • Last reader signals writers • Any writer must wait for all readers • Readers can starve writers • “Updates” can be delayed forever not desirable!
4 5 3 2 1 Writers Take Precedence reader() { while(TRUE) { <other computing>; P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } intreadCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; Semaphore readBlock=1,writeBlock=1 writer() { while(TRUE) { <other computing>; P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
5 4 3 2 1 Writers Take Precedence reader() { while(TRUE) { <other computing>; P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } intreadCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; semaphore readBlock=1,writeBlock=1 writer() { while(TRUE) { <other computing>; P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
6 7 5 4 3 Writers Take Precedence reader() { while(TRUE) { <other computing>; P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } intreadCount=0, writeCount=0; semaphore mutex1=1, mutex2=1; semaphore readBlock=1,writeBlock=1 writer() { while(TRUE) { <other computing>; P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } } • Writers can starve readers • “Reads” can be delayed forever • Not desirable, either !
4 3 2 1 Fair to Readers and Writers ? reader() { while(TRUE) { <other computing>; P(writePending); P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); V(writePending); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; writer() { while(TRUE) { <other computing>; P(writePending); P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); V(writePending); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
4 5 3 2 1 Fair to Readers and Writers ? reader() { while(TRUE) { <other computing>; P(writePending); P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); V(writePending); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; writer() { while(TRUE) { <other computing>; P(writePending); P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); V(writePending); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
4 5 3 Fair to Readers and Writers ? reader() { while(TRUE) { <other computing>; P(writePending); P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); V(writePending); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; writer() { while(TRUE) { <other computing>; P(writePending); P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); V(writePending); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
4 5 6 3 Fair to Readers and Writers ? reader() { while(TRUE) { <other computing>; P(writePending); P(readBlock); P(mutex1); readCount++; if(readCount == 1) P(writeBlock); V(mutex1); V(readBlock); V(writePending); access(resource); P(mutex1); readCount--; if(readCount == 0) V(writeBlock); V(mutex1); } } int readCount = 0, writeCount = 0; semaphore mutex1 = 1, mutex2 = 1; semaphore readBlock = 1, writeBlock = 1, writePending = 1; writer() { while(TRUE) { <other computing>; P(writePending); P(mutex2); writeCount++; if(writeCount == 1) P(readBlock); V(mutex2); P(writeBlock); access(resource); V(writeBlock); V(writePending); P(mutex2) writeCount--; if(writeCount == 0) V(readBlock); V(mutex2); } }
Dining Philosophers while(TRUE) { think(); get2chopsticks(); eat(); }
Dining Philosophers Anything wrong with this code? while(TRUE) { think(); getchopstick-R(); getchopstick-L(); eat(); }
**here The Barbershop Problem semaphore max_capacity = 20, sofa = 4, barber_chair = 3, coord = 3; semaphorecust_ready = 0, finished = 0, leave_b_chair = 0, payment= 0, receipt = 0; void customer () { wait(max_capacity); enter_shop(); wait(sofa); sit_on_sofa(); wait(barber_chair); get_up_from_sofa(); signal(sofa); sit_in_barber_chair; signal(cust_ready); wait(finished); leave_barber_chair(); signal(leave_b_chair); pay(); signal(payment); wait(receipt); exit_shop(); signal(max_capacity) } • void barber() • { • while (true) • { • wait(cust_ready); • wait(coord); • cut_hair(); • signal(coord); • signal(finished); • wait(leave_b_chair); • signal(barber_chair); • } • }
The Barbershop (cont.) void cashier() { while (true) { wait(payment); wait(coord); accept_pay(); signal(coord); signal(receipt); } } void main() { parbegin (customer, . . . 50 times . . . customer, barber, barber, barber, cashier); }