CIS Operating Systems Synchronization Professor Qiang Zeng Fall 2015
Previous class… IPC –Pipes –FIFO (named pipes) –message queues –shared memory Concepts –Race condition, critical section, mutual exclusion Synchronization primitives CIS 5512 – Operating Systems 2
Compare different IPCs IPC methodFeatures PipesCan only be used among parent and child FIFOs (named pipes) Can be referred to by a string, so doesn’t have the limitation above Message queuesUnlike Pipes and FIFOs, they support message boundary and message types Shared memoryData passing doesn’t go through kernel, so it is usually the most efficient one CIS 5512 – Operating Systems 3
Concepts: race condition, critical section, and mutual exclusion lock(); counter++ //critical section unlock(); What goes here? The code below is run simultaneously in multiple threads/processes Race condition exists unless…
Big picture of synchronization primitives Spin locks (busy-waiting locks) Algorithms that do not rely on special instructions (Dekker’, Peterson’s, Bakery) Algorithms based on atomic read-modify- write instructions (test-and-set, xchg)
Solutions based on atomic read-modify- write instructions CIS 5512 – Operating Systems 6 test_and_set(int* p){ int t = *p; *p = 1; return t; } test_and_set(int* p){ int t = *p; *p = 1; return t; } enter_region() { while(test_and_set(&lock) == 1) ; } leave_region() { lock = 0;} enter_region() { while(test_and_set(&lock) == 1) ; } leave_region() { lock = 0;}
Big picture of synchronization primitives 7 Sync. Primitives (or loosely, “locks”) Spin locks (busy-waiting locks) Semaphore: it contains an internal counter indicating the number of resources; API: down()/up() (or, sem_wait/sem_signal) Binary Semaphore is a special semaphore, whose counter value can only be 0 or 1; sometimes it is used as a mutex Conditional Variables: they are used to ease concurrent programming. You have to use it with a mutex. Monitor encapsulates CV and mutex Blocking locks Algorithms that do not rely on special instructions (Dekker’, Peterson’s, Bakery) Algorithms based on atomic read-modify- write instructions (test-and-set, xchg)
Semaphore – avoids busy-waiting CIS 5512 – Operating Systems 8 // Atomic down(S) { // or, P(), sem_wait() if(S.cnt>0) S.cnt--; else put the current process in queue; block the current process; } // Atomic down(S) { // or, P(), sem_wait() if(S.cnt>0) S.cnt--; else put the current process in queue; block the current process; } // Atomic up(S) { // or, V(), sem_signal() if(any process is in S’s wait queue) remove a process from the queue; resume it; else S.cnt++; } // Atomic up(S) { // or, V(), sem_signal() if(any process is in S’s wait queue) remove a process from the queue; resume it; else S.cnt++; }
Using Mutexs and Semaphores: The restroom problem and the bar problem
Binary Semaphore for the restroom problem: mutual exclusion Customers try to enter a single-spot restroom please write code describing the customers Hint: Binary Semaphore, a special semaphore, whose counter value can only be 0 or 1 Here, Binary Semaphore is used as Mutex, a blocking lock for MUTual EXclusion S = 1; // shared among customers (processes) // each customer (process) does the following down(S); // try to enter the restroom; = lock() Use the restroom //critical section up(S); // leave the restroom; = unlock() S = 1; // shared among customers (processes) // each customer (process) does the following down(S); // try to enter the restroom; = lock() Use the restroom //critical section up(S); // leave the restroom; = unlock()
Semaphore for the bar problem Capacity = 100 Many customers try to enter the bar concurrently Please write code describing the customers Caution: a Mutex will not work well; why? CIS 5512 – Operating Systems 11 S = 100; // shared among processes // each process does the following down(S); // try to enter the bar Have fun; up(S); // leave the bar S = 100; // shared among processes // each process does the following down(S); // try to enter the bar Have fun; up(S); // leave the bar
Using Semaphores: A simple signaling problem
Semaphore for signaling Process 0 and Process 1 How to make sure statement A in Process 0 gets executed before statement B in Process 1 Hint: use a semaphore and initialize it as 0 S = 0; // shared // Process 0 A; up(S); S = 0; // shared // Process 0 A; up(S); // Process 1 down(S); B; // Process 1 down(S); B;
Semaphore for signaling CIS 5512 – Operating Systems 14 How to make sure event A1 in Process 0 occurs before B1 in Process 1, and B2 in Process 1 occurs before A2 in Process 0? Hint: use two semaphores S1 = 0; // shared S2 = 0; // shared // Process 0 A1; up(S1); down(S2); A2; S1 = 0; // shared S2 = 0; // shared // Process 0 A1; up(S1); down(S2); A2; // Process 1 down(S1); B1; B2; up(S2); // Process 1 down(S1); B1; B2; up(S2);
Single-slot Producer-Consumer problem Hint: –Consumer.removeItem() has to occur after Producer.fillSlot() –Producer.fillSlot(), once the slot is filled, has to occur after Consumer.removeItem() CIS 5512 – Operating Systems 15 S_slot = 1; // shared S_item = 0; // shared // Producer while(true) { down(S_slot); fillSlot(); up(S_item); } S_slot = 1; // shared S_item = 0; // shared // Producer while(true) { down(S_slot); fillSlot(); up(S_item); } // Consumer while(true) { down(S_item); removeItem(); up(S_slot); } // Consumer while(true) { down(S_item); removeItem(); up(S_slot); }
Using Semaphores: The Producer-Consumer Problem Acknowledgement: some slides courtesy of Dr. Brighten Godfrey
Producer-consumer problem Chefs cook items and put them on a conveyer belt Waiters pick items off the belt
Producer-consumer problem Now imagine many chefs!...and many waiters!
Producer-consumer problem A potential mess!
Producer-consumer problem Chef (Producer)Waiter (Consumer) inserts itemsremoves items Shared resource: bounded buffer Efficient implementation: circular fixed-size buffer
Shared buffer Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr What does the chef do with a new pizza? Where does the waiter take a pizza from? Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Insert pizza insertPtr Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Insert pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Insert pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza removePtr Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Insert pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Insert pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr BUFFER FULL: Producer must wait! Insert pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Remove pizza Chef (Producer)Waiter (Consumer)
Shared buffer insertPtr removePtr Buffer empty: Consumer must be blocked! Remove pizza Chef (Producer)Waiter (Consumer)
Designing a solution Chef (Producer)Waiter (Consumer) Wait for empty slot Insert item Signal item arrival Wait for item arrival Remove item Signal empty slot available What synchronization do we need?
Designing a solution Chef (Producer)Waiter (Consumer) Wait for empty slot Insert item Signal item arrival Wait for item arrival Remove item Signal empty slot available What synchronization do we need? Mutex (shared buffer)
Designing a solution Chef (Producer)Waiter (Consumer) Wait for empty slot Insert item Signal item arrival Wait for item arrival Remove item Signal empty slot available What synchronization do we need? Semaphore (# empty slots)
Designing a solution Chef (Producer)Waiter (Consumer) Wait for empty slot Insert item Signal item arrival Wait for item arrival Remove item Signal empty slot available What synchronization do we need? Semaphore (# filled slots)
Producer-Consumer Code buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; result = buffer[removePtr]; removePtr = (removePtr +1) % N; Critical Section: move insert pointer Critical Section: move remove pointer
Producer-Consumer Code sem_wait(&slots); mutex_lock(&mutex); buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; mutex_unlock(&mutex); sem_post(&items); sem_wait(&items); mutex_lock(&mutex); result = buffer[removePtr]; removePtr = (removePtr +1) % N; mutex_unlock(&mutex); sem_post(&slots); Block if there are no free slots Block if there are no items to take Counting semaphore – check and decrement the number of free slots Counting semaphore – check and decrement the number of available items Done – increment the number of available items Done – increment the number of free slots
Consumer Pseudocode: getItem() sem_wait(&items); pthread_mutex_lock(&mutex); result = buffer[removePtr]; removePtr = (removePtr +1) % N; pthread_mutex_unlock(&mutex); sem_signal(&slots); Error checking/EINTR handling not shown
Producer Pseudocode: putItem(data) sem_wait(&slots); pthread_mutex_lock(&mutex); buffer[ insertPtr ] = data; insertPtr = (insertPtr + 1) % N; pthread_mutex_unlock(&mutex); sem_signal(&items); Error checking/EINTR handling not shown
Readers-Writers Problem
Mutual exclusion problem Problem statement: –Reader threads only read the object –Writer threads modify the object –Writers must have exclusive access to the object –Unlimited number of readers can access the object Occurs frequently in real systems, e.g., –Online airline reservation system –Multithreaded caching Web proxy
void writer(void) { while (1) { sem_wait(&w); /* Critical section */ /* Writing here */ sem_post(&w); } } Writers: int readcnt; /* Initially = 0 */ sem_t mutex, w; /* Both initially = 1 */ Shared: Solution favoring readers
(full code online) void reader(void) { while (1) { sem_wait(&mutex); readcnt++; if (readcnt == 1) /* First reader in */ sem_wait(&w); /* Lock out writers */ sem_post(&mutex); /* Main critical section */ /* Reading would happen here */ sem_wait(&mutex); readcnt--; if (readcnt == 0) /* Last out */ sem_post(&w); /* Let in writers */ sem_post(&mutex); } } Readers: Solution favoring readers
Summary Synchronization: more than just locking a critical section Semaphores useful for counting available resources –sem_wait(): wait for resource only if none available –sem_post(): signal availability of another resource Multiple semaphores / mutexes can work together to solve complex problems