Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 6: Synchronization Tools

Similar presentations


Presentation on theme: "Chapter 6: Synchronization Tools"— Presentation transcript:

1 Chapter 6: Synchronization Tools

2 Chapter 6: Synchronization Tools
Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware Mutex Locks Semaphores Monitors

3 Objectives To present the concept of process synchronization.
To introduce the critical-section problem, whose solutions can be used to ensure the consistency of shared data To present both software and hardware solutions of the critical-section problem To examine several classical process-synchronization problems To explore several tools that are used to solve process synchronization problems

4 Background Processes can execute concurrently
May be interrupted at any time, partially completing execution Concurrent access to shared data may result in data inconsistency Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes Illustration of the problem: Suppose that we wanted to provide a solution to the consumer-producer problem that fills all the buffers. We can do so by having an integer counter that keeps track of the number of full buffers. Initially, counter is set to 0. It is incremented by the producer after it produces a new buffer and is decremented by the consumer after it consumes a buffer.

5 Semaphore wait() and signal() wait(S) { signal(S) {
Synchronization tool that provides more sophisticated ways (than Mutex locks) for process to synchronize their activities. Semaphore S – non-negative integer variable Can only be accessed via two indivisible (atomic) operations wait() and signal() Originally called P() and V() Definition of the wait() operation wait(S) { while (S <= 0) ; // busy wait, spinlock S--; } Definition of the signal() operation signal(S) { S++;

6 Busy waiting problem Busy waiting wastes CPU cycles.
Spinlocks are useful in multiprocessor systems. no context switch is required when a process must wait on a lock. Spinlocks are useful when held for short times Each semaphore has an associated queue of processes/threads wait(S): decrement S. If S = 0, then block until greater than zero Signal(S): increment S by one and wake 1 waiting thread (if any) Classic semaphores have no other operations

7 Two Types of Semaphores
Binary semaphore: like a mutex lock (has a Boolean value) Initialized to 1 A thread performs a wait() until value is 1 and then sets it to 0 Signal() sets value to 1, waking up a waiting thread, if any Counting semaphore: represents a resource with many units available allows threads/process to enter as long as more units are available counter is initialized to N N = number of units available

8 Semaphore Usage – Synchronization Tool
Consider P1 and P2 that require a statement S1 to execute before statement S2 Create a semaphore “synch” initialized to 0 P1: S1; signal(synch); P2: wait(synch); S2;

9 Semaphore Implementation
Must guarantee that no two processes can execute the wait() and signal() on the same semaphore at the same time This is critical section problem In uniprocessor system it can be solved by disabling interrupts during the time the wait() and signal() operations are executing In a multicore environment, interrupts must be disabled on every processing core Disabling interrupts on every core can be a difficult task and can seriously diminish performance Busy waiting moved from the entry section to the critical sections Limit busy waiting to the critical sections of the wait() and signal() operations The critical section is almost never occupied, and busy waiting occurs rarely, and then for only a short time Note that applications may spend lots of time in critical sections and may be frequent which makes busy waiting extremely inefficient

10 Semaphore Implementation with no Busy waiting
With each semaphore there is an associated waiting queue Each entry in a waiting queue has two data items: value (of type integer) pointer to next record in the list typedef struct{ int value; struct process *list; } semaphore; A wait() on a semaphore adds the process to the list of processes A signal() removes one process from the list of waiting processes and awakens that process

11 Implementation with no Busy waiting (Cont.)
wait(semaphore *S) { S->value--; if (S->value < 0) { add this process to S->list; sleep(); } signal(semaphore *S) { S->value++; if (S->value <= 0) { remove a process P from S->list; wakeup(P); Sleep( ) – suspends the process that invokes it and places the process into a waiting queue associated with the semaphore, and the state of the process is switched to the waiting state Wakeup(P) – change the process from waiting state to ready state and place it in the ready queue Wait()/signal() are critical sections, they must be executed atomically Busy waiting is limited only to the critical sections of wait and signal operations, and these are short

12 Deadlock and Starvation
Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes Let S and Q be two semaphores initialized to 1 P P1 wait(S); wait(Q); wait(Q); wait(S); signal(S); signal(Q); signal(Q); signal(S); Starvation – indefinite blocking A process may never be removed from the semaphore queue in which it is suspended Indefinite blocking may occur if we add and remove processes from the list associated with a semaphore in LIFO order. Priority Inversion – Scheduling problem when lower-priority process holds a lock needed by higher-priority process  no progress! Solved via priority-inheritance protocol

13 Two uses of semaphores Mutual exclusion (initially S = 1)
Binary semaphores can be used for mutual exclusion Process Pi: do { wait(S); CriticalSection() signal(S); remainder section } while (1);

14 Two uses of semaphores Scheduling constraints
Locks are fine for mutual exclusion, but what if you want a thread to wait for something? For example, suppose you had to implement Thread::Join, which must wait for a thread to terminate. By setting the initial value to 0 instead of 1, we can implement waiting on a semaphore: Initially S = 0 Fork Thread::Join calls wait() // will wait until something makes // the semaphore positive. Thread finish calls signal() // makes the semaphore positive // and wakes up the thread // waiting in Join.

15 Problems with Semaphores
Incorrect use of semaphores can result in timing errors. Semaphore solution to critical-section problem all processes share a semaphore variable mutex, initialized to 1 What will happen if wait() and signal() are interchanged? signal (mutex) ; criticalSection(); wait (mutex); Several processes may execute in their critical section same time What will happen if a process replaces signal(mutex) with wait(mutex)? wait (mutex) criticalSection() wait (mutex) //results in deadlock Omitting of wait(mutex) or signal(mutex) (or both) Deadlock and starvation are possible.

16 Motivation for monitors and condition variables
Semaphores are a huge step up; just think of trying to do the bounded buffer with only loads and stores. But the problem with semaphores is that they are dual purpose. They’re used for both mutex and scheduling constraints. This makes the code hard to read, and hard to get right. Idea in monitors is to separate these concerns: use locks for mutual exclusion and condition variables for scheduling constraints.

17 Monitors Monitor: a lock and zero or more condition variables for managing concurrent access to shared data Note: Textbook describes monitors as a programming language construct, where the monitor lock is acquired automatically on calling any procedure Some languages like Java, C# provide this natively In Nachos, and in many real-life operating systems, such as Windows NT, OS/2, or Solaris, monitors are used with explicit calls to locks and condition variables.

18 Monitors – book definition
A high-level abstraction that provides a convenient and effective mechanism for process synchronization Abstract data type (ADT), internal variables only accessible by code within the procedure A procedure defined within a monitor can access only those variables declared locally within the monitor and its formal parameters Only one process may be active within the monitor at a time, providing mutual exclusion The monitor construct, as defined so far, is not powerful enough to model some synchronization schemes, we need to define additional synchronization mechanisms monitor monitor-name { // shared variable declarations procedure P1 (…) { …. } procedure Pn (…) {……} Initialization code (…) { … } }

19 Schematic view of a Monitor

20 Monitors Lock: The lock provides mutual exclusion to the shared data. Remember: Lock::Acquire – wait until lock is free, then grab it Lock::Release – unlock, wake up anyone waiting in Acquire Rules for using a lock: Always acquire before accessing shared data structure Always release after finishing with shared data. Lock is initially free. Condition Variable: a queue of threads waiting for something inside a critical section Key idea: make it possible to go to sleep inside critical section by atomically releasing lock at time we go to sleep Contrast to semaphores: Can’t wait inside critical section

21 Monitor with Condition Variables

22 Simple Monitor Example
a (infinite) synchronized list Lock lock; AddToQueue(item) { lock.Acquire(); // lock before using shared data put item on queue; // ok to access shared data lock.Release(); // unlock after done with shared data } RemoveFromQueue() { lock.Acquire(); // lock before using shared data if something on queue // ok to access shared data remove it; lock.Release(); // unlock after done with shared data return item;

23 Condition variables How do we change RemoveFromQueue to wait until something is on the queue? Logically, we want to go to sleep inside of the critical section, but if we hold the lock when we go to sleep, other threads won’t be able to get in to add things to the queue, to wake up the sleeping thread. Condition Variable: a queue of threads waiting for something inside a critical section Key idea: make it possible to go to sleep inside critical section by atomically releasing lock at time we go to sleep

24 Condition variables condition x, y;
Condition variables support three operations: X.Wait() – Release lock, go to sleep, re-acquire lock later, before returning Releasing lock and going to sleep is atomic X.Signal() – Wake up a waiter, if any If no x.wait() on the variable, then it has no effect on the variable Broadcast() – Wake up all waiters Rule: must hold lock when doing condition variable operations Note: In Birrell paper, he says can do signal outside of lock – IGNORE HIM (this is only a performance optimization, and likely to lead you to write incorrect code).

25 Monitor Example (with cond. Variables)
A (infinite) synchronized queue Lock lock; Condition dataready; AddToQueue(item) { lock.Acquire(); // get lock put item on queue; // add item dataready.signal(); // signal any waiters lock.Release(); // release lock } RemoveFromQueue() { lock.Acquire(); // get lock while nothing on queue dataready.wait(&lock); // release lock; go to // sleep; re-acquire lock remove item from queue; lock.Release(); // release lock return item;

26 Mesa vs. Hoare monitors Need to be careful about the precise definition of signal and wait. If process P invokes x.signal(), and process Q is suspended in x.wait(), what should happen next? Mesa-style: (Nachos, most real operating systems) Signal and continue – Q waits until P either leaves the monitor or it waits for another condition Signaler keeps lock, processor Waiter simply put on ready queue, with no special priority (in other words, waiter may have to wait for lock) Hoare-style: (most textbooks) Signal and wait – P waits until Q either leaves the monitor or it waits for another condition Signaler gives up lock, CPU to waiter; waiter runs immediately Waiter gives lock, processor back to signaler when it exits critical section or if it waits again.

27 Mesa vs. Hoare monitors The code for synchronized queuing happens to work with either style, but for many programs it matters which one you are using. With Hoare-style, you can change “while” in RemoveFromQueue to an “if”, because the waiter only gets woken up if there’s an item on the list. With Mesa-style monitors, waiter may need to wait again after being woken up, because some other thread may have acquired the lock, and removed the item, before the original waiting thread gets to the front of the ready queue. This means as a general principle, you almost always need to check the condition after the wait, with Mesa-style monitors (in other words, use a “while” instead of an “if”).

28 Monitor Implementation Using Semaphores
Variables semaphore mutex; // (initially = 1) semaphore next; // (initially = 0) int next_count = 0; Each procedure F will be replaced by wait(mutex); body of F; if (next_count > 0) signal(next) else signal(mutex); Mutual exclusion within a monitor is ensured

29 Monitor Implementation – Condition Variables
For each condition variable x, we have: semaphore x_sem; // (initially = 0) int x_count = 0; The operation x.wait can be implemented as: x_count++; if (next_count > 0) signal(next); else signal(mutex); wait(x_sem); x_count--;

30 Monitor Implementation (Cont.)
The operation x.signal can be implemented as: if (x_count > 0) { next_count++; signal(x_sem); wait(next); next_count--; }

31 Resuming Processes within a Monitor
If several processes queued on condition x, and x.signal() executed, which should be resumed? FCFS frequently not adequate conditional-wait construct of the form x.wait(c) Where c is priority number Process with lowest number (highest priority) is scheduled next

32 Single Resource allocation
Allocate a single resource among competing processes using priority numbers that specify the maximum time a process plans to use the resource R.acquire(t); ... access the resurce; R.release; Where R is an instance of type ResourceAllocator

33 A Monitor to Allocate Single Resource
monitor ResourceAllocator { boolean busy; condition x; void acquire(int time) { if (busy) x.wait(time); busy = TRUE; } void release() { busy = FALSE; x.signal(); initialization code() {

34 End of Chapter 6


Download ppt "Chapter 6: Synchronization Tools"

Similar presentations


Ads by Google