Implementing Mutual Exclusion

Slides:



Advertisements
Similar presentations
Synchronization NOTE to instructors: it is helpful to walk through an example such as readers/writers locks for illustrating the use of condition variables.
Advertisements

Global Environment Model. MUTUAL EXCLUSION PROBLEM The operations used by processes to access to common resources (critical sections) must be mutually.
PROCESS SYNCHRONIZATION READINGS: CHAPTER 5. ISSUES IN COOPERING PROCESSES AND THREADS – DATA SHARING Shared Memory Two or more processes share a part.
Process Synchronization. Module 6: Process Synchronization Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware Semaphores.
Mutual Exclusion.
CH7 discussion-review Mahmoud Alhabbash. Q1 What is a Race Condition? How could we prevent that? – Race condition is the situation where several processes.
CS444/CS544 Operating Systems Synchronization 2/21/2006 Prof. Searleman
CS444/CS544 Operating Systems Synchronization 2/16/2007 Prof. Searleman
5.6 Semaphores Semaphores –Software construct that can be used to enforce mutual exclusion –Contains a protected variable Can be accessed only via wait.
5.6.2 Thread Synchronization with Semaphores Semaphores can be used to notify other threads that events have occurred –Producer-consumer relationship Producer.
Synchronization Principles. Race Conditions Race Conditions: An Example spooler directory out in 4 7 somefile.txt list.c scores.txt Process.
CPS110: Implementing threads/locks on a uni-processor Landon Cox.
U NIVERSITY OF M ASSACHUSETTS, A MHERST Department of Computer Science Emery Berger University of Massachusetts, Amherst Operating Systems CMPSCI 377 Lecture.
Computer Science 162 Discussion Section Week 3. Agenda Project 1 released! Locks, Semaphores, and condition variables Producer-consumer – Example (locks,
Threads and Critical Sections Vivek Pai / Kai Li Princeton University.
9/8/2015cse synchronization-p1 © Perkins DW Johnson and University of Washington1 Synchronization Part 1 CSE 410, Spring 2008 Computer Systems.
CS510 Concurrent Systems Introduction to Concurrency.
U NIVERSITY OF M ASSACHUSETTS, A MHERST Department of Computer Science Emery Berger University of Massachusetts, Amherst Operating Systems CMPSCI 377 Lecture.
Implementing Synchronization. Synchronization 101 Synchronization constrains the set of possible interleavings: Threads “agree” to stay out of each other’s.
Process Synchronization Continued 7.2 Critical-Section Problem 7.3 Synchronization Hardware 7.4 Semaphores.
© Janice Regan, CMPT 300, May CMPT 300 Introduction to Operating Systems Mutual Exclusion.
Semaphores and Bounded Buffer. Semaphores  Semaphore is a type of generalized lock –Defined by Dijkstra in the last 60s –Main synchronization primitives.
Chapter 28 Locks Chien-Chung Shen CIS, UD
COMP 111 Threads and concurrency Sept 28, Tufts University Computer Science2 Who is this guy? I am not Prof. Couch Obvious? Sam Guyer New assistant.
Operating Systems ECE344 Ashvin Goel ECE University of Toronto Mutual Exclusion.
COSC 3407: Operating Systems Lecture 7: Implementing Mutual Exclusion.
CS 162 Discussion Section Week 2 (9/16 – 9/20). Who am I? Kevin Klues Office Hours:
CS 3204 Operating Systems Godmar Back Lecture 7. 12/12/2015CS 3204 Fall Announcements Project 1 due on Sep 29, 11:59pm Reading: –Read carefully.
CS399 New Beginnings Jonathan Walpole. 2 Concurrent Programming & Synchronization Primitives.
CSCI-375 Operating Systems Lecture Note: Many slides and/or pictures in the following are adapted from: slides ©2005 Silberschatz, Galvin, and Gagne Some.
U NIVERSITY OF M ASSACHUSETTS A MHERST Department of Computer Science Computer Systems Principles Synchronization Emery Berger and Mark Corner University.
1 Previous Lecture Overview  semaphores provide the first high-level synchronization abstraction that is possible to implement efficiently in OS. This.
Implementing Lock. From the Previous Lecture  The “too much milk” example shows that writing concurrent programs directly with load and store instructions.
CS510 Concurrent Systems Jonathan Walpole. Introduction to Concurrency.
Implementing Mutual Exclusion Andy Wang Operating Systems COP 4610 / CGS 5765.
Mutual Exclusion -- Addendum. Mutual Exclusion in Critical Sections.
CS162 Section 2. True/False A thread needs to own a semaphore, meaning the thread has called semaphore.P(), before it can call semaphore.V() False: Any.
CS162 Operating Systems and Systems Programming Lecture 4 Synchronization, Atomic operations, Locks, Semaphores September 12, 2011 Anthony D. Joseph and.
Process Synchronization. Concurrency Definition: Two or more processes execute concurrently when they execute different activities on different devices.
CS703 - Advanced Operating Systems
CSE 120 Principles of Operating
Sarah Diesburg Operating Systems COP 4610
CS703 – Advanced Operating Systems
Atomic Operations in Hardware
Atomic Operations in Hardware
Monitors, Condition Variables, and Readers-Writers
Lecture 12: Peterson’s Solution and Hardware Support
CSCI 511 Operating Systems Chapter 5 (Part B) Mutual Exclusion, Semaphores Dr. Frank Li.
January 31, 2011 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 4 Synchronization, Atomic operations,
Sarah Diesburg Operating Systems COP 4610
Andy Wang Operating Systems COP 4610 / CGS 5765
Lecture 14: Pthreads Mutex and Condition Variables
Jonathan Walpole Computer Science Portland State University
UNIVERSITY of WISCONSIN-MADISON Computer Sciences Department
Critical section problem
Coordination Lecture 5.
Grades.
February 6, 2013 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 5 Semaphores, Conditional Variables.
Implementing Mutual Exclusion
Lecture 14: Pthreads Mutex and Condition Variables
Kernel Synchronization II
September 12, 2012 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 5 Semaphores, Conditional Variables.
Lecture 12: Peterson’s Solution and Hardware Support
CSE 451: Operating Systems Autumn 2003 Lecture 7 Synchronization
CSE 451: Operating Systems Autumn 2005 Lecture 7 Synchronization
CSE 451: Operating Systems Winter 2003 Lecture 7 Synchronization
CSE 153 Design of Operating Systems Winter 19
CS333 Intro to Operating Systems
Process/Thread Synchronization (Part 2)
September 19, 2018 Prof. Ion Stoica
Presentation transcript:

Implementing Mutual Exclusion Andy Wang Operating Systems COP 4610 / CGS 5765

From the Previous Lecture The “too much milk” example shows that writing concurrent programs directly with load and store instructions (i.e., C assignment statements) is tricky Programmers want to use higher-level operations, such as locks

Ways of Implementing Locks All implementations require some level of hardware support Locking primitives High-level atomic operations Locks, semaphores, monitors, send and receive Low-level atomic operations Load/store, interrupt disables, test_and_set

Atomic Memory Load and Store C assignment statements Examples: “too much milk” solutions

Disable Interrupts (for Uniprocessors) On a uniprocessor, An operation is atomic as long as a context switch does not occur in the middle of an operation Solution 1 Lock::Acquire() { // disable interrupts; } Lock::Release() { // enable interrupts;

An Example lockAcquire(); if (no milk) { // get milk } lockRelease();

Problems with Solution 1 A user-level program may not re-enable interrupts The kernel can no longer regain the control No guarantees on the duration of interrupts; bad for real-time systems Solution 1 will not work for more complex scenarios (nested locks)

Solution 2 class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts }

Solution 2 The lock is initially FREE. class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts } The lock is initially FREE.

Solution 2 Check the lock value while interrupts are disabled. class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts } Check the lock value while interrupts are disabled.

Solution 2 Re-enable interrupts inside the loop, so someone may have a class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts } Re-enable interrupts inside the loop, so someone may have a chance to unlock.

Solution 2 Disable the interrupts again before checking the lock. class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts } Disable the interrupts again before checking the lock.

Solution 2 If no one is holding the lock, grab the lock. class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts while (value != FREE) { // enable interrupts value = BUSY; Lock::Release() { // disable interrupts value = FREE; // enable interrupts } If no one is holding the lock, grab the lock.

Problems with Solution 2 It works for a single processor It does not work on a multi-processor machine Other CPUs can still enter the critical section

The test_and_set Operation test_and_set also works on multiprocessors Atomically reads a memory location Sets it to 1 Returns the old value of memory location

The test_and_set Operation value = 0; Lock::Acquire() { // while the previous value is BUSY, loop while (test_and_set(value) == 1); } Lock::Release() {

Common Problems with Mentioned Approaches Busy-waiting: consumption of CPU cycles while a thread is waiting for a lock Very inefficient Can be avoided with a waiting queue

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Thread 1 tries to grab the lock.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts No more busy waiting…

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Grab the lock.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Thread 1 goes on computing.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Thread 2 tries to grab the lock.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts The lock is busy…

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Put the thread 2 on a waiting queue.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Go to sleep. Wait for the princess charming…Context switch within the sleep.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Say thread 1 wants to release the lock (interrupts are already disabled by thread 2).

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Hello? Is someone waiting there? <echo> Is someone waiting there? Thread 2 is waiting.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Put thread 2 on ready queue; context switch.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Thread 2: Who kissed me? I don’t do mornings…

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Thread 2 is done with its computation.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Suppose no one else is waiting.

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Release the lock. (Thread 1 has finished its work, so it’s okay.)

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Warp 9, engage (let’s get out of here)…

Locks Using Interrupt Disables, Without Busy-Waiting class Lock { int value = FREE; } Lock::Acquire() { // disable interrupts if (value != FREE) { // Queue the thread // Go to sleep } else { value = BUSY; // enable interrupts Lock::Release() { // disable interrupts if (someone is waiting) { // wake a thread // Put it on ready queue } else { value = FREE; } // enable interrupts Eventually, the kernel will context switch back to thread 1…Thread 1: What happened?

So, What’s Going On? Interrupt disable and enable operations occur across context switches (at the steady state)

So, What’s Going On? Thread A Thread B Context Disable interrupts Return from sleep Enable interrupts Context switch Disable interrupts Sleep Disable interrupts Sleep Return from sleep Enable interrupts Context switch

Locks Using test_and_set, With Minimal Busy-Waiting Impossible to use test_and_set to avoid busy-waiting However, waiting can be minimized with a waiting queue

Locks Using test_and_set, With Minimal Busy-Waiting class Lock { int value = FREE; int guard = 0; } Lock::Acquire() { while (test_and_set(guard)); if (value != FREE) { // queue the thread // guard = 0 and sleep } else { value = BUSY; guard = 0; Lock::Release() { while (test_and_set(guard)); if (anyone waiting) { // wake up one thread // put it on ready queue } else { value = FREE; } guard = 0;