CS 333 Introduction to Operating Systems Class 4 Concurrent Programming and Synchronization Primitives

Similar documents
CS 333 Introduction to Operating Systems. Class 4 Concurrent Programming and Synchronization Primitives

CS 333 Introduction to Operating Systems. Class 3 Threads & Concurrency. Jonathan Walpole Computer Science Portland State University

CS 333 Introduction to Operating Systems. Class 3 Threads & Concurrency. Jonathan Walpole Computer Science Portland State University

CS533 Concepts of Operating Systems. Jonathan Walpole

Interprocess Communication and Synchronization

CS510 Operating System Foundations. Jonathan Walpole

CS333 Intro to Operating Systems. Jonathan Walpole

CS510 Operating System Foundations. Jonathan Walpole

POSIX threads CS 241. February 17, Copyright University of Illinois CS 241 Staff

Unix. Threads & Concurrency. Erick Fredj Computer Science The Jerusalem College of Technology

CS 153 Design of Operating Systems Winter 2016

Lecture 5 Threads and Pthreads II

Synchronization. CSE 2431: Introduction to Operating Systems Reading: Chapter 5, [OSC] (except Section 5.10)

CS3733: Operating Systems

Concurrency. Chapter 5

Thread. Disclaimer: some slides are adopted from the book authors slides with permission 1

CS 5523 Operating Systems: Midterm II - reivew Instructor: Dr. Tongping Liu Department Computer Science The University of Texas at San Antonio

C09: Process Synchronization

PROCESS SYNCHRONIZATION

SYNCHRONIZATION M O D E R N O P E R A T I N G S Y S T E M S R E A D 2. 3 E X C E P T A N D S P R I N G 2018

Recap: Thread. What is it? What does it need (thread private)? What for? How to implement? Independent flow of control. Stack

Chapter 6: Process Synchronization

Operating Systems. Operating Systems Summer 2017 Sina Meraji U of T

Pre-lab #2 tutorial. ECE 254 Operating Systems and Systems Programming. May 24, 2012

Lesson 6: Process Synchronization

Semaphore. Originally called P() and V() wait (S) { while S <= 0 ; // no-op S--; } signal (S) { S++; }

Lecture 8: September 30

Introduction to OS Synchronization MOS 2.3

Synchronization for Concurrent Tasks

Lecture 5: Synchronization w/locks

POSIX Threads. HUJI Spring 2011

CS 3305 Intro to Threads. Lecture 6

Learning Outcomes. Concurrency and Synchronisation. Textbook. Concurrency Example. Inter- Thread and Process Communication. Sections & 2.

Chapter 6: Process Synchronization

Semaphores. Jinkyu Jeong Computer Systems Laboratory Sungkyunkwan University

Parallel Programming with Threads

UNIX Input/Output Buffering

Concurrency and Synchronisation

CS-345 Operating Systems. Tutorial 2: Grocer-Client Threads, Shared Memory, Synchronization

High-level Synchronization

Chapter 6: Process Synchronization. Module 6: Process Synchronization

Chapter 6: Synchronization. Chapter 6: Synchronization. 6.1 Background. Part Three - Process Coordination. Consumer. Producer. 6.

Operating Systems. Lecture 4 - Concurrency and Synchronization. Master of Computer Science PUF - Hồ Chí Minh 2016/2017

Process Management And Synchronization

Processes, Threads, SMP, and Microkernels

CSC 1600: Chapter 6. Synchronizing Threads. Semaphores " Review: Multi-Threaded Processes"

COP 4225 Advanced Unix Programming. Synchronization. Chi Zhang

Concurrency: a crash course

CS420: Operating Systems. Process Synchronization

Concurrency and Synchronisation

Reminder from last time

Background. The Critical-Section Problem Synchronisation Hardware Inefficient Spinning Semaphores Semaphore Examples Scheduling.

Synchronization Principles I

Module 6: Process Synchronization

Systèmes d Exploitation Avancés

Synchronization: semaphores and some more stuff. Operating Systems, Spring 2018, I. Dinur, D. Hendler and R. Iakobashvili

Locks. Dongkun Shin, SKKU

CS 3723 Operating Systems: Final Review

Synchronization 1. Synchronization

CS 162 Operating Systems and Systems Programming Professor: Anthony D. Joseph Spring Lecture 8: Semaphores, Monitors, & Condition Variables

Synchronization. Disclaimer: some slides are adopted from the book authors slides with permission 1

Synchronization Primitives

Processes Prof. James L. Frankel Harvard University. Version of 6:16 PM 10-Feb-2017 Copyright 2017, 2015 James L. Frankel. All rights reserved.

Lecture Outline. CS 5523 Operating Systems: Concurrency and Synchronization. a = 0; b = 0; // Initial state Thread 1. Objectives.

pthreads Announcement Reminder: SMP1 due today Reminder: Please keep up with the reading assignments (see class webpage)

Agenda. Process vs Thread. ! POSIX Threads Programming. Picture source:

Global Environment Model

Review: Easy Piece 1

Dealing with Issues for Interprocess Communication

Module 6: Process Synchronization. Operating System Concepts with Java 8 th Edition

Process Synchronization(2)

Chapter 5: Process Synchronization. Operating System Concepts Essentials 2 nd Edition

Introduction to PThreads and Basic Synchronization

Chapter 5: Process Synchronization. Operating System Concepts 9 th Edition

Operating Systems. Designed and Presented by Dr. Ayman Elshenawy Elsefy

Synchronization. Before We Begin. Synchronization. Credit/Debit Problem: Race Condition. CSE 120: Principles of Operating Systems.

Semaphores. To avoid busy waiting: when a process has to wait, it will be put in a blocked queue of processes waiting for the same event

Semaphores. Semaphores. Semaphore s operations. Semaphores: observations

CS 471 Operating Systems. Yue Cheng. George Mason University Fall 2017

Chapter 6: Process Synchronization. Operating System Concepts 8 th Edition,

Lecture 6 (cont.): Semaphores and Monitors

CSCI 8530 Advanced Operating Systems. Part 5 Process Coordination and Synchronization

Midterm on next week Tuesday May 4. CS 361 Concurrent programming Drexel University Fall 2004 Lecture 9

Threads. Threads The Thread Model (1) CSCE 351: Operating System Kernels Witawas Srisa-an Chapter 4-5

CS 5523: Operating Systems

Concurrency. On multiprocessors, several threads can execute simultaneously, one on each processor.

CSE 4/521 Introduction to Operating Systems

Synchronization. Dr. Yingwu Zhu

Chapter 6: Synchronization. Operating System Concepts 8 th Edition,

CS240: Programming in C. Lecture 18: PThreads

Concurrency. On multiprocessors, several threads can execute simultaneously, one on each processor.

Process Synchronization

Chapter 5: Process Synchronization. Operating System Concepts 9 th Edition

Chapter 6: Process Synchronization

Synchronization Principles

Threading and Synchronization. Fahd Albinali

CPSC 341 OS & Networks. Threads. Dr. Yingwu Zhu

Concurrency. On multiprocessors, several threads can execute simultaneously, one on each processor.

Implementing Mutual Exclusion. Sarah Diesburg Operating Systems CS 3430

Introduction to Operating Systems

Transcription:

CS 333 Introduction to Operating Systems Class 4 Concurrent Programming and Synchronization Primitives Jonathan Walpole Computer Science Portland State University 1

What does a typical thread API look like? POSIX standard threads (Pthreads) First thread exists in main(), typically creates the others pthread_create (thread,attr,start_routine,arg) Returns new thread ID in thread Executes routine specified by start_routine with argument specified by arg Exits on return from routine or when told explicitly 2

Thread API (continued) pthread_exit (status) Terminates the thread and returns status to any joining thread pthread_join (threadid,status) Blocks the calling thread until thread specified by threadid terminates Return status from pthread_exit is passed in status One way of synchronizing between threads pthread_yield () Thread gives up the CPU and enters the run queue 3

Using create, join and exit primitives 4

An example Pthreads program #include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *PrintHello(void *threadid) { printf("\n%d: Hello World!\n", threadid); pthread_exit(null); } int main (int argc, char *argv[]) { pthread_t threads[num_threads]; int rc, t; for(t=0; t<num_threads; t++) { printf("creating thread %d\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); if (rc) { printf("error; return code from pthread_create() is %d\n", rc); exit(-1); } } pthread_exit(null); } Program Output Creating thread 0 Creating thread 1 0: Hello World! 1: Hello World! Creating thread 2 Creating thread 3 2: Hello World! 3: Hello World! Creating thread 4 4: Hello World! For more examples see: http://www.llnl.gov/computing/tutorials/pthreads 5

Pros & cons of threads Pros Overlap I/O with computation! Cheaper context switches Better mapping to shared memory multiprocessors Cons Potential thread interactions due to concurrency Complexity of debugging Complexity of multi-threaded programming Backwards compatibility with existing code 6

Concurrency Assumptions: Example: Two or more threads Each executes in (pseudo) parallel We can t predict exact running speeds The threads can interact via access to shared variables One thread writes a variable The other thread reads from the same variable Problem non-determinism: The relative order of one thread s reads and the other thread s writes determines the end result! 7

Race conditions What is a race condition? Why do race conditions occur? 8

Race conditions A simple multithreaded program with a race: i++; 9

Race conditions A simple multithreaded program with a race:... load i to register; increment register; store register to i;... 10

Race conditions Why did this race condition occur? two or more threads have an inconsistent view of a shared memory region (I.e., a variable) values of memory locations replicated in registers during execution context switches at arbitrary times during execution threads can see stale memory values in registers 11

Race Conditions Race condition: whenever the output depends on the precise execution order of the processes! What solutions can we apply? prevent context switches by preventing interrupts make threads coordinate with each other to ensure mutual exclusion in accessing critical sections of code 12

Mutual exclusion conditions No two processes simultaneously in critical section No assumptions made about speeds or numbers of CPUs No process running outside its critical section may block another process No process must wait forever to enter its critical section 13

Using mutual exclusion for critical sections 14

How can we enforce mutual exclusion? What about using locks? s solve the problem of exclusive access to shared data. Acquiring a lock prevents concurrent access Expresses intention to enter critical section Assumption: Each shared data item has an associated lock All threads set the lock before accessing the shared data Every thread releases the lock after it is done 15

Acquiring and releasing locks Thread B Thread C Thread A Thread D Free 16

Acquiring and releasing locks Thread B Thread C Thread A Thread D Free 17

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 18

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 19

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 20

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 21

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 22

Acquiring and releasing locks Thread A Thread B Thread C Thread D Set 23

Acquiring and releasing locks Thread A Thread B Thread C Thread D Set 24

Acquiring and releasing locks Thread B Thread C Thread A Unlock Thread D Set 25

Acquiring and releasing locks Thread B Thread C Thread A Unlock Thread D Set 26

Acquiring and releasing locks Thread A Thread B Thread C Thread D Free 27

Acquiring and releasing locks Thread A Thread B Thread C Thread D Free 28

Acquiring and releasing locks Thread A Thread B Thread C Thread D Set 29

Acquiring and releasing locks Thread A Thread B Thread C Thread D Set 30

Acquiring and releasing locks Thread B Thread C Thread A Thread D Set 31

Mutual exclusion (mutex) locks An abstract data type Used for synchronization The mutex is either: ed ( the lock is held ) Unlocked ( the lock is free ) 32

Mutex lock operations (mutex) Acquire the lock if it is free and continue Otherwise wait until it can be acquired Unlock (mutex) Release the lock If there are waiting threads wake up one of them 33

How to use a mutex? Shared data: Mutex my; 1 repeat 2 (my); 3 critical section 4 Unlock(my); 5 remainder section 6 until FALSE 1 repeat 2 (my); 3 critical section 4 Unlock(my); 5 remainder section 6 until FALSE 34

But how can we implement a mutex? What if the lock is a binary variable How would we implement the lock and unlock procedures? 35

But how can we implement a mutex? and Unlock operations must be atomic! Many computers have some limited hardware support for setting locks Atomic Test and Set instruction Atomic compare and swap operation These can be used to implement mutex locks 36

Test-and-set-lock instruction (TSL, tset) A lock is a single word variable with two values 0 = FALSE = not locked 1 = TRUE = locked Test-and-set does the following atomically: Get the (old) value Set the lock to TRUE Return the old value If the returned value was FALSE... Then you got the lock!!! If the returned value was TRUE... Then someone else has the lock (so try again later) 37

Test and set lock P1 FALSE 38

Test and set lock P1 test FALSE FALSE = Available!! 39

Test and set lock P1 set TRUE 40

Test and set lock P1 P2 P3 TRUE TRUE TRUE TRUE TRUE TRUE TRUE P4 41

Test and set lock P1 P2 P3 TRUE TRUE TRUE TRUE TRUE TRUE TRUE P4 42

Test and set lock P1 P2 P3 TRUE TRUE TRUE TRUE TRUE TRUE TRUE P4 43

Test and set lock P1 P2 P3 FALSE FALSE TRUE P4 44

Test and set lock P1 P2 P3 TRUE FALSE TRUE TRUE P4 45

Test and set lock P1 P2 P3 TRUE FALSE TRUE P4 46

Test and set lock P1 P2 P3 TRUE TRUE TRUE TRUE TRUE P4 47

Using TSL directly for critical sections 1 repeat 2 while(tsl(lock)) 3 no-op; I 1 repeat 2 while(tsl(lock)) 3 no-op; J 4 critical section 4 critical section 5 = FALSE; 5 = FALSE; 6 remainder section 6 remainder section 7 until FALSE 7 until FALSE Guarantees that only one thread at a time will enter its critical section 48

Implementing a mutex with TSL 1 repeat 2 while(tsl(mylock)) 3 no-op; 4 critical section 5 mylock = FALSE; (mylock) Unlock (mylock) 6 remainder section 7 until FALSE Note that processes are busy while waiting this kind of mutex is called a spin lock 49

Busy waiting Also called polling or spinning The thread consumes CPU cycles to evaluate when the lock becomes free! Problem on a single CPU system... A busy-waiting thread can prevent the lock holder from running & completing its critical section & releasing the lock! time spent spinning is wasted on a single CPU system Why not block instead of busy wait? 50

Blocking synchronization primitives Sleep Put a thread to sleep Thread becomes BLOCKED Wakeup Move a BLOCKED thread back onto Ready List Thread becomes READY (or RUNNING) Yield Put calling thread on ready list and schedule next thread Does not BLOCK the calling thread! Just gives up the current time-slice 51

But how can these be implemented? In User Programs: System calls to the kernel In Kernel: Calls to the thread scheduler routines 52

Concurrency control in user programs User threads call sleep and wakeup system calls Scheduler routines in the kernel implement sleep and wakeup they manipulate the ready list but the ready list is shared data the code that manipulates it is a critical section What if a timer interrupt occurs during a sleep or wakeup call? Problem: How can scheduler routines be programmed to execute correctly in the face of concurrency? 53

Concurrency in the kernel Solution 1: Disable interrupts during critical sections Ensures that interrupt handling code will not run but what if there are multiple CPUs? Solution 2: Use mutex locks based on TSL for critical sections Ensures mutual exclusion for all code that follows that convention... but what if your hardware doesn t have TSL? 54

Disabling interrupts Disabling interrupts in the OS vs disabling interrupts in user processes why not allow user processes to disable interrupts? is it ok to disable interrupts in the OS? what precautions should you take? 55

Disabling interrupts in the kernel Scenario 1: A thread is running; wants to access shared data Disable interrupts Access shared data ( critical section ) Enable interrupts 56

Disabling interrupts in the kernel Problem: Interrupts are already disabled and a thread wants to access the critical section...using the above sequence... Ie. One critical section gets nested inside another 57

Disabling interrupts in the kernel Problem: Interrupts are already disabled. Thread wants to access critical section using the previous sequence... Save previous interrupt status (enabled/disabled) Disable interrupts Access shared data ( critical section ) Restore interrupt status to what it was before 58

Disabling interrupts is not enough on MPs Disabling interrupts during critical sections Ensures that interrupt handling code will not run But what if there are multiple CPUs? A thread on a different CPU might make a system call which invokes code that manipulates the ready queue Using a mutex lock (based on TSL) for critical sections Ensures mutual exclusion for all code that follows that convention 59

Some tricky issues The interrupt handling code that saves interrupted state is a critical section It could be executed concurrently if multiple almost simultaneous interrupts happen Interrupts must be disabled during this (short) time period to ensure critical state is not lost What if this interrupt handling code attempts to lock a mutex that is held? What happens if we sleep with interrupts disabled? What happens if we busy wait (spin) with interrupts disabled? 60

Implementing mutex locks without TSL If your CPU did not have TSL, how would you implement blocking mutex lock and unlock calls using interrupt disabling? this is your next Blitz project! 61

An Example Synchronization Problem 62

The Producer-Consumer Problem An example of the pipelined model One thread produces data items Another thread consumes them Use a bounded buffer between the threads The buffer is a shared resource Code that manipulates it is a critical section Must suspend the producer thread if the buffer is full Must suspend the consumer thread if the buffer is empty 63

Is this busy-waiting solution correct? thread producer { while(1){ // Produce char c while (count==n) { no_op } buf[inp] = c InP = InP + 1 mod n count++ } } thread consumer { while(1){ while (count==0) { no_op } c = buf[outp] OutP = OutP + 1 mod n count-- // Consume char } } n-1 0 2 1 Global variables: char buf[n] int InP = 0 // place to add int OutP = 0 // place to get int count 64

This code is incorrect! The count variable can be corrupted: Increments or decrements may be lost! Possible Consequences: Both threads may spin forever Buffer contents may be over-written What is this problem called? 65

This code is incorrect! The count variable can be corrupted: Increments or decrements may be lost! Possible Consequences: Both threads may sleep forever Buffer contents may be over-written What is this problem called? Race Condition Code that manipulates count must be made into a??? and protected using??? 66

This code is incorrect! The count variable can be corrupted: Increments or decrements may be lost! Possible Consequences: Both threads may sleep forever Buffer contents may be over-written What is this problem called? Race Condition Code that manipulates count must be made into a critical section and protected using mutual exclusion! 67

Some more problems with this code What if buffer is full? Producer will busy-wait On a single CPU system the consumer will not be able to empty the buffer What if buffer is empty? Consumer will busy-wait On a single CPU system the producer will not be able to fill the buffer We need a solution based on blocking! 68

Producer/Consumer with Blocking 1 st attempt 0 thread producer { 1 while(1) { 2 // Produce char c 3 if (count==n) { 4 sleep(full) 5 } 6 buf[inp] = c; 7 InP = InP + 1 mod n 8 count++ 9 if (count == 1) 10 wakeup(empty) 11 } 12 } 0 thread consumer { 1 while(1) { 2 while (count==0) { 3 sleep(empty) 4 } 5 c = buf[outp] 6 OutP = OutP + 1 mod n 7 count--; 8 if (count == n-1) 9 wakeup(full) 10 // Consume char 11 } 12 } n-1 0 1 2 Global variables: char buf[n] int InP = 0 // place to add int OutP = 0 // place to get int count 69

Use a mutex to fix the race condition in this code 0 thread producer { 1 while(1) { 2 // Produce char c 3 if (count==n) { 4 sleep(full) 5 } 6 buf[inp] = c; 7 InP = InP + 1 mod n 8 count++ 9 if (count == 1) 10 wakeup(empty) 11 } 12 } 0 thread consumer { 1 while(1) { 2 while (count==0) { 3 sleep(empty) 4 } 5 c = buf[outp] 6 OutP = OutP + 1 mod n 7 count--; 8 if (count == n-1) 9 wakeup(full) 10 // Consume char 11 } 12 } n-1 0 1 2 Global variables: char buf[n] int InP = 0 // place to add int OutP = 0 // place to get int count 70

Problems Sleeping while holding the mutex causes deadlock! Releasing the mutex then sleeping opens up a window during which a context switch might occur again risking deadlock How can we release the mutex and sleep in a single atomic operation? We need a more powerful synchronization primitive 71

Semaphores An abstract data type that can be used for condition synchronization and mutual exclusion What is the difference between mutual exclusion and condition synchronization? 72

Semaphores An abstract data type that can be used for condition synchronization and mutual exclusion Mutual exclusion only one at a time in a critical section Condition synchronization wait until invariant holds before proceeding signal when invariant holds so others may proceed 73

Semaphores An abstract data type containing an integer variable (S) Two operations: Wait (S) and Signal (S) Alternative names for the two operations Wait(S) = Down(S) = P(S) Signal(S) = Up(S) = V(S) Current version of Blitz uses Down/Up as names for its semaphore operations 74

Semaphores Wait (S) decrement S by 1 test value of S if S is negative sleep until signaled Signal (S) increment S by 1 signal/wakeup a waiting thread Both Wait () and Signal () are assumed to be atomic!!! A kernel implementation must ensure atomicity 75

Variation: Binary Semaphores Counting Semaphores same as just semaphore Binary Semaphores a specialized use of semaphores the semaphore is used to implement a Mutex 76

Variation: Binary Semaphores Counting Semaphores same as just semaphore Binary Semaphores a specialized use of semaphores the semaphore is used to implement a Mutex the count will always be either <=0 = locked 1 = unlocked 77

Using Semaphores for Mutex semaphore mutex = 1 -- unlocked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 78

Using Semaphores for Mutex semaphore mutex = 0 -- locked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 79

Using Semaphores for Mutex semaphore mutex = 0 --locked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 80

Using Semaphores for Mutex semaphore mutex = 0 -- locked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 81

Using Semaphores for Mutex semaphore mutex = 0 -- locked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 82

Using Semaphores for Mutex semaphore mutex = 1 -- unlocked This thread can now be released! 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 83

Using Semaphores for Mutex semaphore mutex = 0 -- locked 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE 1 repeat 2 wait(mutex); 3 critical section 4 signal(mutex); 5 remainder section 6 until FALSE Thread A Thread B 84

Exercise: producer/consumer with semaphores Global variables semaphore full_buffs =?; semaphore empty_buffs =?; char buff[n]; int InP, OutP; 0 thread producer { 1 while(1){ 2 // Produce char c... 3 buf[inp] = c 4 InP = InP + 1 mod n 5 } 6 } 0 thread consumer { 1 while(1){ 2 c = buf[outp] 3 OutP = OutP + 1 mod n 4 // Consume char... 5 } 6 } 85

Counting semaphores in producer/consumer Global variables semaphore full_buffs = 0; semaphore empty_buffs = n; char buff[n]; int InP, OutP; 0 thread producer { 1 while(1){ 2 // Produce char c... 3 wait(empty_buffs) 4 buf[inp] = c 5 InP = InP + 1 mod n 6 signal(full_buffs) 7 } 8 } 0 thread consumer { 1 while(1){ 2 wait(full_buffs) 3 c = buf[outp] 4 OutP = OutP + 1 mod n 5 signal(empty_buffs) 6 // Consume char... 7 } 8 } 86

Continue next time 87

Implementing semaphores Wait () and Signal () are assumed to be atomic 88

Implementing semaphores Wait () and Signal () are assumed to be atomic How can we ensure that they are atomic? 89

Implementing semaphores Wait () and Signal () are assumed to be atomic How can we ensure that they are atomic? Implement Wait() and Signal() as system calls? how can the kernel ensure Wait() and Signal() are completed atomically? Same solutions as before Disable interrupts, or Use TSL-based mutex 90

Blitz code for Semaphore.wait method Wait () var oldintstat: int oldintstat = SetInterruptsTo (DISABLED) if count == 0x80000000 FatalError ("Semaphore count underflowed during 'Wait operation") EndIf count = count 1 if count < 0 waitingthreads.addtoend (currentthread) currentthread.sleep () endif oldintstat = SetInterruptsTo (oldintstat) endmethod 91

Blitz code for Semaphore.wait method Wait () var oldintstat: int oldintstat = SetInterruptsTo (DISABLED) if count == 0x80000000 FatalError ("Semaphore count underflowed during 'Wait operation") EndIf count = count 1 if count < 0 waitingthreads.addtoend (currentthread) currentthread.sleep () endif oldintstat = SetInterruptsTo (oldintstat) endmethod 92

Blitz code for Semaphore.wait method Wait () var oldintstat: int oldintstat = SetInterruptsTo (DISABLED) if count == 0x80000000 FatalError ("Semaphore count underflowed during 'Wait operation") EndIf count = count 1 if count < 0 waitingthreads.addtoend (currentthread) currentthread.sleep () endif oldintstat = SetInterruptsTo (oldintstat) endmethod 93

Blitz code for Semaphore.wait method Wait () var oldintstat: int oldintstat = SetInterruptsTo (DISABLED) if count == 0x80000000 FatalError ("Semaphore count underflowed during 'Wait operation") EndIf count = count 1 if count < 0 waitingthreads.addtoend (currentthread) currentthread.sleep () endif oldintstat = SetInterruptsTo (oldintstat) endmethod 94

Blitz code for Semaphore.signal method Signal () var oldintstat: int t: ptr to Thread oldintstat = SetInterruptsTo (DISABLED) if count == 0x7fffffff FatalError ("Semaphore count overflowed during 'Signal' operation") endif count = count + 1 if count <= 0 t = waitingthreads.remove () t.status = READY readylist.addtoend (t) endif oldintstat = SetInterruptsTo (oldintstat) endmethod 95

Blitz code for Semaphore.signal method Signal () var oldintstat: int t: ptr to Thread oldintstat = SetInterruptsTo (DISABLED) if count == 0x7fffffff FatalError ("Semaphore count overflowed during 'Signal' operation") endif count = count + 1 if count <= 0 t = waitingthreads.remove () t.status = READY readylist.addtoend (t) endif oldintstat = SetInterruptsTo (oldintstat) endmethod 96

Blitz code for Semaphore.signal method Signal () var oldintstat: int t: ptr to Thread oldintstat = SetInterruptsTo (DISABLED) if count == 0x7fffffff FatalError ("Semaphore count overflowed during 'Signal' operation") endif count = count + 1 if count <= 0 t = waitingthreads.remove () t.status = READY readylist.addtoend (t) endif oldintstat = SetInterruptsTo (oldintstat) endmethod 97

Blitz code for Semaphore.signal method Signal () var oldintstat: int t: ptr to Thread oldintstat = SetInterruptsTo (DISABLED) if count == 0x7fffffff FatalError ("Semaphore count overflowed during 'Signal' operation") endif count = count + 1 if count <= 0 t = waitingthreads.remove () t.status = READY readylist.addtoend (t) endif oldintstat = SetInterruptsTo (oldintstat) endmethod 98

But what is currentthread.sleep ()? If sleep stops a thread from executing, how, where, and when does it return? which thread enables interrupts following sleep? the thread that called sleep shouldn t return until another thread has called signal! but how does that other thread get to run? where exactly does the thread switch occur? Trace down through the Blitz code until you find a call to switch() Switch is called in one thread but returns in another! See where registers are saved and restored 99

Semaphores using atomic instructions As we saw earlier, hardware provides special atomic instructions for synchronization test and set lock (TSL) compare and swap (CAS) etc Semaphore can be built using atomic instructions 1. build mutex locks from atomic instructions 2. build semaphores from mutex locks 100

Building yielding mutex locks using TSL Mutex_lock: TSL REGISTER,MUTEX copy mutex to register and set mutex to 1 CMP REGISTER,#0 was mutex zero? JZE ok if it was zero, mutex is unlocked, so return CALL thread_yield mutex is busy, so schedule another thread JMP mutex_lock try again later Ok: RET return to caller; enter critical section Mutex_unlock: MOVE MUTEX,#0 RET store a 0 in mutex return to caller 101

Building spinning mutex locks using TSL Mutex_lock: TSL REGISTER,MUTEX copy mutex to register and set mutex to 1 CMP REGISTER,#0 was mutex zero? JZE ok if it was zero, mutex is unlocked, so return CALL thread_yield mutex is busy, so schedule another thread JMP mutex_lock try again later Ok: RET return to caller; enter critical section Mutex_unlock: MOVE MUTEX,#0 RET store a 0 in mutex return to caller 102

To block or not to block? Spin-locks do busy waiting wastes CPU cycles on uni-processors Why? Blocking locks put the thread to sleep may waste CPU cycles on multi-processors Why? 103

Building semaphores using mutex locks Problem: Implement a counting semaphore Signal () Wait ()...using just Mutex locks 104

How about two blocking mutex locks? var cnt: int = 0 -- Signal count var m1: Mutex = unlocked -- Protects access to cnt m2: Mutex = locked -- ed when waiting Wait (): (m1) cnt = cnt 1 if cnt<0 Unlock(m1) (m2) else Unlock(m1) endif Signal(): (m1) cnt = cnt + 1 if cnt<=0 Unlock(m2) endif Unlock(m1) 105

How about two blocking mutex locks? var cnt: int = 0 -- Signal count var m1: Mutex = unlocked -- Protects access to cnt m2: Mutex = locked -- ed when waiting Wait (): (m1) cnt = cnt 1 if cnt<0 Unlock(m1) (m2) else Unlock(m1) endif Signal(): (m1) cnt = cnt + 1 if cnt<=0 Unlock(m2) endif Unlock(m1) 106

Oops! How about this then? var cnt: int = 0 -- Signal count var m1: Mutex = unlocked -- Protects access to cnt m2: Mutex = locked -- ed when waiting Wait (): (m1) cnt = cnt 1 if cnt<0 (m2) Unlock(m1) else Unlock(m1) endif Signal(): (m1) cnt = cnt + 1 if cnt<=0 Unlock(m2) endif Unlock(m1) 107

Oops! How about this then? var cnt: int = 0 -- Signal count var m1: Mutex = unlocked -- Protects access to cnt m2: Mutex = locked -- ed when waiting Wait (): (m1) cnt = cnt 1 if cnt<0 (m2) Unlock(m1) else Unlock(m1) endif Signal(): (m1) cnt = cnt + 1 if cnt<=0 Unlock(m2) endif Unlock(m1) 108

Ok! Lets have another try! var cnt: int = 0 -- Signal count var m1: Mutex = unlocked -- Protects access to cnt m2: Mutex = unlocked -- ed when waiting Wait (): (m2) (m1) cnt = cnt 1 if cnt>0 Unlock(m2) endif Unlock(m1) Signa;(): (m1) cnt = cnt + 1 if cnt=1 Unlock(m2) endif Unlock(m1) is this solution valid? 109

What about this solution? Mutex m1, m2; // binary semaphores int C = N; // N is # locks int W = 0; // W is # wakeups Wait(): (m1); C = C 1; if (C<0) Unlock(m1); (m2); (m1); W = W 1; if (W>0) Unlock(m2); endif; else Unlock(m1); endif; Signal(): (m1); C = C + 1; if (C<=0) W = W + 1; Unlock(m2); endif; Unlock(m1); 110

Quiz What is a race condition? How can we protect against race conditions? Can locks be implemented simply by reading and writing to a binary variable in memory? How can a kernel make synchronization-related system calls atomic on a uniprocessor? Why wouldn t this work on a multiprocessor? Why is it better to block rather than spin on a uniprocessor? Why is it sometimes better to spin rather than block on a multiprocessor? 111