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

Interprocess Communication and Synchronization

CS533 Concepts of Operating Systems. Jonathan Walpole

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

CS 153 Design of Operating Systems Winter 2016

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

C09: Process Synchronization

Chapter 6: Process Synchronization

Concurrency. Chapter 5

Concurrency: a crash course

PROCESS SYNCHRONIZATION

CS420: Operating Systems. 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

CS3733: Operating Systems

Synchronization Principles I

Chapter 6: Process Synchronization

Synchronization for Concurrent Tasks

COP 4225 Advanced Unix Programming. Synchronization. Chi Zhang

Introduction to OS Synchronization MOS 2.3

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

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

Lecture 5: Synchronization w/locks

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

Process Management And Synchronization

Lecture 8: September 30

Reminder from last time

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

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

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

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

High-level Synchronization

Synchronization 1. Synchronization

Locks. Dongkun Shin, SKKU

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

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

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

Chapter 6: Process Synchronization. Module 6: Process Synchronization

Dealing with Issues for Interprocess Communication

Lesson 6: Process Synchronization

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

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

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

Concurrency and Synchronisation

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

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

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

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

Global Environment Model

Module 6: Process Synchronization

Concurrency and Synchronisation

Implementing Mutual Exclusion. Sarah Diesburg Operating Systems CS 3430

Lecture 6 (cont.): Semaphores and Monitors

CS 333 Introduction to Operating Systems. Class 6 Monitors and Message Passing. Jonathan Walpole Computer Science Portland State University

Chapter 7: Process Synchronization!

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

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

CSE 120 Principles of Operating Systems Spring 2016

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

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

Lecture #7: Implementing Mutual Exclusion

Process Synchronization(2)

Chapter 2 Processes and Threads. Interprocess Communication Race Conditions

Process Synchronization

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

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

Interprocess Communication By: Kaushik Vaghani

Semaphores. Jinkyu Jeong Computer Systems Laboratory Sungkyunkwan University

Chapter 5 Asynchronous Concurrent Execution

Synchronization 1. Synchronization

Background. Old Producer Process Code. Improving the Bounded Buffer. Old Consumer Process Code

Process Synchronization

Concurrent Processes Rab Nawaz Jadoon

Process & Thread Management II. Queues. Sleep() and Sleep Queues CIS 657

Process & Thread Management II CIS 657

CHAPTER 6: PROCESS SYNCHRONIZATION

Introduction to Operating Systems Prof. Chester Rebeiro Department of Computer Science and Engineering Indian Institute of Technology, Madras

CS510 Operating System Foundations. Jonathan Walpole

Process Synchronization

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

Multitasking / Multithreading system Supports multiple tasks

Last Class: Synchronization

Lecture 6. Process Synchronization

Page 1. Goals for Today" Atomic Read-Modify-Write instructions" Examples of Read-Modify-Write "

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

Chapter 6: Process Synchronization

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

7: Interprocess Communication

Chapter 5 Concurrency: Mutual Exclusion. and. Synchronization. Operating Systems: Internals. and. Design Principles

Chapter 5 Concurrency: Mutual Exclusion and Synchronization

Today: Synchronization. Recap: Synchronization

The University of Texas at Arlington

IV. Process Synchronisation

What's wrong with Semaphores?

Threading and Synchronization. Fahd Albinali

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

UNIX Input/Output Buffering

Concept of a process

Introduction to Operating Systems

The University of Texas at Arlington

Transcription:

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

Concurrent programming Assumptions: 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 Example: 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! 2

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

Counter increment race condition Incrementing a counter (load, increment, store) Context switch can occur after load and before increment! 4

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 5

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 6

Using mutual exclusion for critical sections 7

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 8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 ) 25

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 26

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 27

But how can we implement a mutex? and Unlock operations must be atomic! Can we just set and clear a binary lock variable in memory? 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 28

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) 29

Test and set lock P1 FALSE 30

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

Test and set lock P1 set TRUE 32

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

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

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

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

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

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

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

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 40

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 41

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? 42

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 43

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

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? 45

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? 46

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? 47

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 48

Disabling interrupts in the kernel Scenario 2: 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 49

Disabling interrupts in the kernel Scenario 2: 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 50

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 51

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? 52

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! 53

An Example Synchronization Problem 54

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 55

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 56

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? 57

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??? 58

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! 59

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! 60

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 61

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 62

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 63

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? 64

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 65

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 66

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 67

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

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 69

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 70

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 71

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 72

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 73

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 74

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 75

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 76

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 } 77

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 } 78

Continue next time 79

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

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

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 82

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 83

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 84

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 85

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 86

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 87

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 88

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 89

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 90

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 91

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 92

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 93

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 94

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? 95

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

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) 97

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) 98

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) 99

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) 100

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? 101

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); 102

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? 103