Synchronization Fall 2016 Reading

• Chapter 2, Sec 2.3, “Modern Operating Systems, Fourth Ed.”, Andrew S. Tanenbaum • you may skip 2.3.9 and 2.3.10

• “POSIX Threads Programming”, available at: https:// computing.llnl.gov/tutorials/pthreads/

• Andrew D. Birrell, “An Introduction to Programming with Threads”, available at: https://birrell.org/andrew/papers/035- Threads.pdf

Fall 2016 Operating Systems 2 Parallelism vs Concurrency

• A parallel system can perform more than one task simultaneously • multicore, multiprocessor, GPU • A concurrent system supports more than one task by allowing all the tasks to make progress • one processor

Fall 2016 Operating Systems 3 Can All Code be Parallelized? int params[10] int params[10] for(i=0;i<10;i++) for(i=0;i<10;i++)

{ {

dosomething(params[i]); val = docomplicated(params[i],val);

} }

can be parallelized cannot be parallelized

Fall 2016 Operating Systems 4 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Case Study

• Hardware Solution

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 5 Independent Threads

• A that cannot affect or be affected by any other threads

• Its state is not shared

• The input state determines its result —> deterministic

• Reproducible

• Stop and continue without “side effects”

Fall 2016 Operating Systems 6 Communication Between Processes

• Sharing Storage

• Main Memory

• Files

• Messages

Fall 2016 Operating Systems 7 Cooperating Threads

• These threads share state

• Their behaviour is nondeterministic as it depends on relative execution of threads

• Their behaviour might be irreproducible

Fall 2016 Operating Systems 8 Cooperating Threads: Example 1

• x and y are shared variables between processes A and B

• Their initial values are zeros

Process A B

x=1 y=2

x=y+1 y=y*2

which order is correct?

Fall 2016 Operating Systems 9 Cooperating Threads: Example 2

• i is a shared variable between processes A and B

Process A Process B

i=0; i=0; while(i<10) while(i>-10) { { i++; i--; } } printf(“A wins”); printf(“B wins”);

which order is correct?

Fall 2016 Operating Systems 10 Case Study: Print Spooling

Processes A and B want to access at the same time

the next file to be printed processes A and B are queuing files for printing the next free slot in the directory

which order is correct?

Fall 2016 Operating Systems 11 Case Study:Too Much Milk Problem

Person A Person B

3:00 Look in Fridge: no milk

3:05 Leave for store

3:10 Arrive at store Look in Fridge: no milk

3:15 Leave at store Leave for store

3:20 Arrive at Home, put milk away Arrive at store

3:25 Leave at store

3:30 Arrive at Home, too much milk!

which order is correct?

Fall 2016 Operating Systems 12 Atomic Operations

• Atomic operation: can be performed entirely with no interruption

• an operation that can be done entirely before the interrupts are checked

• Mostly, memory load and store operations are atomic

• Many of the other operations are not atomic:i++, i--,x=y+3

• Atomic operations are needed to make higher level constructs to simulate atomicity

Fall 2016 Operating Systems 13 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Case Study

• Hardware Solution

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 14 Race Condition

• Race condition: Two or more threads (processes) are reading and writing shared data and the final result depends on the relative order of executing these threads (processes)

• Debugging is hard!

• Some final answers are not correct

Fall 2016 Operating Systems 15

• Order of interleaving between threads:

• Irrelevant: operations are independent

• Relevant: operations are dependent

• Any possible interleaving should result in a correct answer

• Synchronization: using atomic operations to ensure correct operations of cooperating threads

• Correct interleaving between threads

• Controls order of execution of threads

Fall 2016 Operating Systems 16 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Case Study

• Hardware Solution

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 17 Critical Region (Critical Section)

• Critical Region: a section of code (several operations) in which only one thread may be executing at a given time

• shared memory is (or other shared resources are) accessed in this section of the code

• the objective is that this section be executed atomically with no interruptions —> no two threads are executing in this section at the same time

• Mutual exclusion: a mechanism used to ensure that only one thread is executing in a critical section

• typically done through locking mechanism, for example, leave a note on the refrigerator before shopping for milk

Fall 2016 Operating Systems 18 Example: Critical Region

Solution 1 Solution 2 Solution 3

1 Enter_Critical_Section if (milk == 0) { Enter_Critical_Section

2 if (milk == 0) { Enter_Critical_Section if (milk == 0) {

3 buyMilk() buyMilk() Exit_Critical_Section

4 } Exit_Critical_Section ……………

5 Exit_Critical_Section } Enter_Critical_Section

6 buyMilk()

Exit_Critical_Section

}

Fall 2016 Operating Systems 19 Example: Critical Region

Solution 1 Solution 2 Solution 3

1 Enter_Critical_Section if (milk == 0) { Enter_Critical_Section

2 if (milk == 0) { Enter_Critical_Section if (milk == 0) {

3 buyMilk() buyMilk() Exit_Critical_Section

4 } Exit_Critical_Section ……………

5 Exit_Critical_Section } Enter_Critical_Section

6 buyMilk()

Exit_Critical_Section

}

Fall 2016 Operating Systems 20 Conditions for a Good Mutual Exclusion Solution

• No two processes may be simultaneously inside their critical regions

• No assumptions may be made about speeds or the number of CPUs

• No process running outside its critical region may block any process

• No process should have to wait forever to enter its critical region

Fall 2016 Operating Systems 21 Summary

• Atomic operation: can be performed entirely with no interruption

• Race condition: Final result depend on the relative order of executing threads

• Critical Section: a section of code in which only one thread may be executing at a given time

• Mutual exclusion: a mechanism used to create critical section

• Synchronization: using atomic operations to ensure correct operations of cooperating threads

Fall 2016 Operating Systems 22 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Case Study

• Hardware Solution

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 23 Mutual Exclusion Mechanisms

• Hardware:

• Disable interrupts

• Special instructions busy waiting • Software:

• Peterson’s solution

• Semaphores

• Mutex

• Monitors

• Message passing

Fall 2016 Operating Systems 24 Revisiting the Too Much Milk Problem

Person A Person B

3:00 Look in Fridge: no milk

3:05 Leave for store

3:10 Arrive at store Look in Fridge: no milk

3:15 Leave at store Leave for store

3:20 Arrive at Home, put milk away Arrive at store

3:25 Leave at store

3:30 Arrive at Home, too much milk!

Fall 2016 Operating Systems 25 Too Much Milk Problem — Attempt 1

Person A & B

1 if (milk == 0) {

2 if (note == 0) {

3 note = 1

4 buyMilk()

5 note = 0

6 }

7 }

Fall 2016 Operating Systems 26 Variables

• Use a shared (lock) variable, initially 0

• When a process wants to enter its critical region

• it tests the lock

• if the lock is 0, set the lock to 1 and enters the critical region

• if the lock is 1, wait until it becomes 0

• Therefore, 0 means that no process is in its critical region, and 1 means that a process is in its critical region

• Does this work?

Fall 2016 Operating Systems 27 Too Much Milk Problem — Attempt 2

Person A Person B

1 while (note == 0) { while (note == 1) {

2 if (milk == 0) { if (milk == 0) {

3 buyMilk() buyMilk()

4 } }

5 note = 1 note = 0

6 } }

Note meaning: A buys if no note, B buys if there is a note.

Fall 2016 Operating Systems 28 Strict Alternation

• Use an integer variable turn, initially 0, keeps track of whose turn it is to enter the critical region

• Procesdure:

• process 0 inspects turn, finds it to be 0, and enters its critical region

• process 1 finds turn to be 0, it continues to wait until it changes to 1

• A process changes the value of turn before leaving the critical section

• Advantage: avoids races

• Disadvantage: violates condition 3

• Note: spin lock is a lock that uses busy waiting

Fall 2016 Operating Systems 29 Too Much Milk Problem — Attempt 3

Person A Person B

1 noteA = 1 noteB = 1

2 if (noteB == 0) { if (noteA == 0) {

3 if (milk == 0) { if (milk == 0) {

4 buyMilk() buyMilk()

5 } }

6 } }

7 noteA = 0 noteB = 0

Use separate notes for A and B

Fall 2016 Operating Systems 30 Too Much Milk Problem — Attempt 4

Person A Person B

1 noteA = 1 noteB = 1

2 if (noteB == 0) { while (noteA == 1) {

3 if (milk == 0) { //do nothing

4 buyMilk() }

5 } if (milk == 0) {

6 } buyMilk()

7 noteA = 0 }

noteB = 0

Fall 2016 Operating Systems 31 Too Much Milk Problem — Attempt 4 — Comments

• Works — hurray !

• Disadvantages:

• Asymmetric solution

• Involves busy-waiting (Continuously testing a variable until some value appears)

Fall 2016 Operating Systems 32 Peterson’s Algorithm

Credit: Andrew Tanenbaum, Modern Operating Systems: 4th ED

Fall 2016 Operating Systems 33 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Hardware Solution

• Case Study

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 34 Hardware: Disabling Interrupts

• Mechanism of each process:

• disable interrupts when entering the critical region

• enable interrupts just before leaving the critical region

• Disadvantages:

• It is unwise to allow user processes to switch off interrupts

• Not suitable for multiprocessors

• Usage: the kernel can use it while updating processes information

Fall 2016 Operating Systems 35 Hardware: Disabling Interrupts — Example

Solution - Disabling Interrupts

1 Disable-Interrupts

2 if (milk == 0) {

3 buyMilk()

4 }

5 Enable-Interrupts

6

Fall 2016 Operating Systems 36 Hardware Instructions

• Atomic instruction, no interruption while the processor is executing it

• the memory bus is locked (different from disabling interrupts!)

• Test and Set Lock (TSL)

• read a memory address into a register

• set the value in the memory address to nonzero (one)

• XCHG

• exchange the location of two locations atomically (e.g. memory and register)

Fall 2016 Operating Systems 37 Test and Set Lock (TSL)

• Entering and leaving critical section using the TSL instruction

Fall 2016 Operating Systems 38 XCHG

• Entering and leaving critical section using the XCHG instruction

Fall 2016 Operating Systems 39 Hardware Instructions — Advantages

• Suitable for any number of processes

• Simple and easy to verify

• Can support multiple critical sections, where each critical section will have to have its variable

Fall 2016 Operating Systems 40 Hardware Instructions — Disadvantages

• Busy waiting (Why it is bad? When is it OK?)

• Starvation is possible

• definition: some processes are not able to enter the critical section

• example: P1 and P2 keep entering the critical section, while P3 is unable to enter it

is possible progress ? progress

• definition: two or more processes are unable to proceed

• Example: P1 is executing in the critical section, P2 is chosen to execute because it has higher priority, P2 is busy waiting

Fall 2016 Operating Systems 41 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Hardware Solution

• Case Study: Producer Consumer

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 42 Case Stude: Producer-Consumer Problem (AKA Bounded Buffer Problem)

• Two processes share a fixed size buffer

• The producer process adds items to the buffer

• The consumer process removes items from the buffer

• Rules:

• The producer and consumer cannot access the buffer at the same time

• If the buffer is full, a producer cannot add new items to the buffer

• If the buffer is empty, a consumer cannot remove an item from the buffer

Fall 2016 Operating Systems 43 Credit: William Stallings, Operating Systems: Internals and Design Principles, 7th ED

Fall 2016 Operating Systems 44 The producer-consumer problem with a fatal race condition

Fall 2016 Operating Systems 45 Race Condition in the Solution?

• Case 1:

• buffer is empty, consumer read count and it is 0

• switch to producer

• producer insert item in the buffer, increment count to 1

• wakeup the consumer (lost!)

• switch to consumer

• check the value read earlier, sleep (forever!)

• Case 2:

• updating count simultaneously

Fall 2016 Operating Systems 46 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Hardware Solution

• Case Study: Producer Consumer

• Semaphores

• Mutexes and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 47 Semaphores

• Idea:

• A process blocks instead of busy waiting

• Use a variable to count wakeup signals

• Semaphore:

• a synchronization variable

• a queue of waiting processes

• Semaphore operations (atomic):

• Signal (V, up): increment the semaphore variable and wake up any waiting processes

• Wait (P, down): decrement the semaphore variable and wait in a queue if it becomes negative

Fall 2016 Operating Systems 48 Semaphore Primitives

Note: semWait = down = P, semSignal = up = V

Credit: William Stallings, Operating Systems: Internals and Design Principles, 7th ED

Fall 2016 Operating Systems 49 Semaphores — Too Much Milk

Person A & B

1 semaphore OKToBuyMilk initialized to 1

2 down(&OKToBuyMilk)

3 if (milk == 0) {

4 buyMilk()

5 }

6 up(&OKToBuyMilk)

Binary Semaphores are initialized to 1 and used by two or more processes to ensure that only one of them can enter its critical region at the same time

Fall 2016 Operating Systems 50 Producer-Consumer Using Semaphores

Fall 2016 Operating Systems 51 Implementing Semaphores

• Semaphores has to be atomic

• Semaphore operations are implemented as system calls

• use disabling interrupt if 1 CPU

• use hardware instruction (e.g. TSL or XCHG) if multiple CPUs

• Questions:

• Is disabling interrupt OK?

• Is busy waiting OK?

Fall 2016 Operating Systems 52 Semaphore Usage

• Two usage:

• Mutual exclusion (e.g. mutex semaphore in the producer- consumer problem solution)

• Synchronization (e.g. full and empty semaphores in the producer-consumer problem solution)

Fall 2016 Operating Systems 53 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Hardware Solution

• Case Study: Producer Consumer

• Semaphores

• Mutex and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 54 Mutex

• A mutex (lock) is a shared variable that can be in one of two states: locked, unlocked

• Notes:

• Similar to semaphore except that they do not have counting feature

• A mechanism that provides mutual exclusion

• Implemented in thread libraries such as Pthread

• Can be implemented in user space (?) using the instructions TSL or XCHG

Fall 2016 Operating Systems 55 Mutex Operations

• mutex_lock (acquire):

• Mark the lock as owned by the current thread

• If the lock is not free (owned by another thread), then wait (!)

• mutex_unlock (release):

• Given that the current thread owns the lock, it can make it free

No kernel calls are needed!

Fall 2016 Operating Systems 56 Implementation of mutex_lock and mutex_unlock

Fall 2016 Operating Systems 57 Using Mutex Operations for Mutual Exclusion

• Usage:

• Call mutex-lock before accessing critical section.

• If mutex is unlocked, the operation succeeds and the thread access critical section

• If mutex is already locked, the thread blocks

• When the thread in critical section finishes, it calls mutex_unlock

Fall 2016 Operating Systems 58 Too Much Milk Problem

Solution - Mutex

1 mutex_lock

2 if (milk == 0) {

3 buyMilk()

4 }

5 mutex_unlock

6

Fall 2016 Operating Systems 59 Producer Consumer - Mutex - Initialization

#include #include #include

#define MAX_ELEM 10 //buffer int buffer[MAX_ELEM]; int count = 0, head = 0, tail = 0; //mutex pthread_mutex_t pc_mutex;

Fall 2016 Operating Systems 60 Producer Consumer - Mutex - Produce

void * produce(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 61 Producer Consumer - Mutex - Consume void * consume(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 62 Producer Consumer - Mutex - Main

int main() { pthread_t producer, consumer; //initialize mutex pthread_mutex_init(&pc_mutex, NULL); //create producer thread pthread_create(&producer, NULL, produce, NULL); //create consumer thread pthread_create(&consumer, NULL, consume, NULL); //wait for threads to finish pthread_join(producer, NULL); pthread_join(consumer, NULL); //cleanup pthread_mutex_destroy(&pc_mutex); /*free up the mutex*/ //exit main thread pthread_exit(NULL); }

Fall 2016 Operating Systems 63 Revisiting Producer Consumer Solution Using Mutex

• Empty/full cases is not handled

• Solutions:

• Attempt 1: use busy waiting

• Attempt 2: use condition variables

Fall 2016 Operating Systems 64 Producer Consumer - Mutex - Produce - Attempt 1 void * produce(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 65 Producer Consumer - Mutex - Consume - Attempt 1 void * consume(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 66 Condition Variables

• Objective: allow threads to block (wait) due to some condition not being met

• Operations for creating and destroying conditions:

• pthread_cond_init (condition,attr)

• pthread_cond_destroy (condition)

• pthread_condattr_init (attr)

• pthread_condattr_destroy (attr)

Fall 2016 Operating Systems 67 Condition Variables Usage

• Operations for Waiting and Signalling threads:

• pthread_cond_wait (condition,mutex): release mutex, put thread to sleep until condition is signalled

• pthread_cond_signal (condition): if any threads are waiting on condition, wake up one of them. When thread wakes up, it needs to re-acquire the mutex

• pthread_cond_broadcast (condition): same as , except that it wakes up all waiting threads on the condition

• Usage example: lock a mutex, wait on a condition variable, possibly block (unlock mutex) and get signaled later

Fall 2016 Operating Systems 68 Condition Variables Usage - Notes

• Notes:

• After signalling a thread, it will wait in a queue until the signalling thread releases the lock

• After a thread wakes up, there is no guarantee that the condition is still valid. The condition is needed to be checked again!

• Unlike semaphores, conditions have no memory. If a signal is sent to a condition and no threads are waiting, the signal is lost

Fall 2016 Operating Systems 69 Producer Consumer - Mutex - Initialization - Attempt 2

#include #include #include

#define MAX_ELEM 10 //buffer int buffer[MAX_ELEM]; int count = 0, head = 0, tail = 0; //mutex pthread_mutex_t pc_mutex; //condition variables pthread_cond_t cond_availData, cond_availSpace;

Fall 2016 Operating Systems 70 Producer Consumer - Mutex - Produce Attempt 2 void * produce(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 71 Producer Consumer - Mutex - Consume - Attempt 2 void * consume(void* threadarg) { int i; for(i=0;i

Fall 2016 Operating Systems 72 Producer Consumer - Mutex - Main - Attempt 2 int main() { pthread_t producer, consumer; //initialize mutex and condition variables pthread_mutex_init(&pc_mutex, NULL); pthread_cond_init(&cond_availData, NULL); /* Initialize consumer condition variable */ pthread_cond_init(&cond_availSpace, NULL); /* Initialize producer condition variable */ //create producer thread pthread_create(&producer, NULL, produce, NULL); //create consumer thread pthread_create(&consumer, NULL, consume, NULL); //wait for threads to finish pthread_join(producer, NULL); pthread_join(consumer, NULL); //cleanup pthread_mutex_destroy(&pc_mutex); /*free up the mutex*/ pthread_cond_destroy(&cond_availData); /* Free up consumer condition variable */ pthread_cond_destroy(&cond_availSpace); /* Free up producer condition variable */ //exit main thread pthread_exit(NULL); }

Fall 2016 Operating Systems 73 Outline

• Interprocess Communication

• Race Conditions

• Critical Regions

• Mutual Exclusion

• Hardware Solution

• Case Study: Producer Consumer

• Semaphores

• Mutex and Condition Variables

• More Mutual Exclusion Mechanisms

Fall 2016 Operating Systems 74 Monitors

• Why use a higher-level synchronization primitive such as monitors?

• A monitor is a collection of: procedures, variables, and data structures

• Processes cannot access the monitor’s data structures

• Processes can only call the monitor procedures

• Only one process can be active in a monitor at any instant

• therefore, mutual execution is guaranteed

• Condition variables with two operations wait and signal are introduced

Fall 2016 Operating Systems 75 Implementing Monitors

• The compiler is aware of the monitors and therefore it guarantees:

• only one process can be executing in the monitor at any time

• the compiler uses primitives such as mutex and binary semaphores to implement monitors

• a process calling a signal must exit the monitor, therefore a signal can only appear at the end of a procedure

• Note: condition variables are not counters and therefore signals is lost if no processes are waiting for it

• Note: wait and signal are not similar to sleep and wakeup that we have used in the first solution of producer/consumer problem

Fall 2016 Operating Systems 76 Structure of a Monitor

Credit: William Stallings, Operating Systems: Internals and Design Principles, 7th ED

Fall 2016 Operating Systems 77 Producer Consumer - Monitors

Fall 2016 Operating Systems 78 Producer Consumer - Monitors

Fall 2016 Operating Systems 79 Message Passing

• Suitable for distributed systems

• Two primitives that are implemented through system calls:

• send(destination, &message): sends a message to destination

• receive(source, &message): receives a message from source

Fall 2016 Operating Systems 80 Design Issues for Message-Passing Systems

• A receive can block waiting for a message from the source, or return with error if a message is not ready

• What to do about lost messages

• use acknowledgment

• duplicate messages

• Using mailbox to hold the messages

• If both send and receive are , this strategy is called rendezvous

Fall 2016 Operating Systems 81 Producer Consumer - Message Passing

Fall 2016 Operating Systems 82 Thank You!