Exploring Condition Variables & Reader-Writer Problem in Monitors, Slides of Computer Numerical Control

The concept of monitors, which are used for synchronization in computer systems. Monitors consist of locks and condition variables, enabling mutual exclusion and scheduling constraints. Various aspects of monitor usage, including mesa-style and hoare-style semantics, producer-consumer scenarios, and the readers/writers problem. It also discusses the implementation of monitors using semaphores and condition variables.

Typology: Slides

2010/2011

Uploaded on 10/07/2011

christina
christina 🇺🇸

4.6

(23)

393 documents

1 / 16

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
1
Monitors
Arvind Krishnamurthy
Spring 2004
Monitors Recap
nMonitors contain:
n“lock” for mutual exclusion
n“condition variables” for scheduling constraints
nMonitor usage:
nObtain lock
nPerform tasks. If certain scheduling constraints are not met,
release lock and sleep till appropriate conditions are met.
nSleeping threads are woken up by “signal” and “broadcast”
operations
nRelease lock when thread exits critical section
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff

Partial preview of the text

Download Exploring Condition Variables & Reader-Writer Problem in Monitors and more Slides Computer Numerical Control in PDF only on Docsity!

Monitors

Arvind Krishnamurthy

Spring 2004

Monitors Recap

n Monitors contain:

n “lock” for mutual exclusion

n “condition variables” for scheduling constraints

n Monitor usage:

n Obtain lock

n Perform tasks. If certain scheduling constraints are not met,

release lock and sleep till appropriate conditions are met.

n Sleeping threads are woken up by “signal” and “broadcast”

operations

n Release lock when thread exits critical section

Synchronized queue

n Rule: must hold lock when doing condition variable

operations

AddToQueue()

lock.Acquire();

put item on queue;

condition.signal();

lock.Release();

RemoveFromQueue()

lock.Acquire();

while nothing on queue

condition.wait(&lock);

// release lock; go to

// sleep; reacquire lock

remove item from queue;

lock->Release();

return item;

Mesa-style vs. Hoare-style

n Mesa-style (Nachos, most real OS):

n Signaler keeps lock, processor

n Waiter simply put on ready queue, with no special priority

(in other words, waiter may have to wait for lock again)

n Hoare-style (most theory, textbook):

n Signaler passes lock, CPU to waiter; waiter runs immediately

n Waiter gives lock, processor back to signaler when it exits critical

section or if it waits again

n For Mesa-semantics, you always need to check the

condition after wait (use “while”). For Hoare-semantics you

can change it to “if”

Monitor Summary

General template for using monitors:

lock.Acquire();

ready = 1;

signal(cond);

lock.Release();

lock.Acquire();

while (!ready) {

wait(cond);

lock.Release();

Issue 1:

n Wait = release lock; sleep; obtain lock

n “release lock + sleep” needs to be atomic

lock.Acquire();

ready = 1;

signal(cond);

lock.Release();

lock.Acquire();

while (!ready) {

lock.Release();

sleep on cond;

lock.Release();

Thread T1 Thread T

Issue 2:

n If wait does not automatically acquire the lock when it

returns, does that lead to errors?

n Is it ok for wait to be just an atomic “release lock + sleep”

lock.Acquire();

ready = 1;

signal(cond);

lock.Release();

lock.Acquire();

while (!ready) {

wait(cond);

lock.Acquire();

lock.Release();

Thread T1 Thread T

Issue 3:

n Does the waker require mutex?

ready = 1;

signal(cond);

lock.Acquire();

while (!ready) {

wait(cond);

lock.Release();

Thread T1 Thread T

Readers/writers problem

n Motivation

n shared database (e.g., bank balances / airline seats)

n Two classes of users:

n Readers --- never modify database

n Writers --- read and modify database

n Using a single lock on the database would be overly restrictive

n want many readers at the same time

n only one writer at the same time

n Constraints

n Readers can access database when no writers (Condition okToRead)

n Writers can access database when no readers or writers (Condition

okToWrite)

n Only one thread manipulates state variable at a time

Design Specification

n Reader

n wait until no writers

n access database

n check out - wake up waiting writer

n Writer

n wait until no readers or writers

n access database

n check out --- wake up waiting readers or writer

n Lock and condition variables: okToRead, okToWrite

Solving readers/writers

Reader() {

lock.Acquire();

WR++;

while (AW > 0)

okToRead.Wait(&lock);

WR--;

AR++;

lock.Release();

Access DB;

lock.Acquire();

AR--;

if (AR == 0 && WW > 0)

okToWrite.Signal(&lock);

lock.Release();

Writer() {

lock.Acquire();

WW++;

while ((AW+AR) > 0)

okToWrite.Wait(&lock);

WW --;

AW++;

lock.Release();

Access DB;

lock.Acquire();

AW--;

if (WW > 0) okToWrite.Signal(&lock);

else if (WR > 0) okToRead.Broadcast(&lock);

lock.Release();

One-way-bridge problem

n Problem definition

n a narrow light-duty bridge on a public highway

n traffic cross in one direction at a time

n at most 3 vehicles on the bridge at the same time (otherwise it will

collapse)

n Each car is represented as one thread:

OneVechicle (int direc)

ArriveBridge(direc);

… cross the bridge …

ExitBridge(direc);

Implementing Monitors

n Can we use semaphores to implement condition variables?

n Simple attempt:

Wait() { semaphore->P(); }

Signal() { semaphore->V(); }

n Solution is not relinquishing the lock:

lock.Acquire();

while (!condition)

Wait();

lock.Release();

lock.Acquire();

Signal();

lock.Release();

Second Attempt

n Use one semaphore for each condition variable

n Release the lock during wait:

Wait(Lock *lock) {

lock->Release();

semaphore->P();

lock->Acquire();

Signal() { semaphore->V(); }

n Is this solution correct?

Peek at the waiting queue

n Perform a check during signal:

Wait(Lock *lock) {

lock->Release();

semaphore->P();

lock->Acquire();

Signal()

if semaphore queue is not empty

semaphore->V();

n Well, it is cheating! But is it correct?

Implementing Monitors

Using one semaphore for each waiting thread --- making sure it indeed gets

the message when it is signalled.

class Condition { List waitQueue; }

Condition::Wait(Lock* lock) {

Semaphore *w;

w = new Semaphore (0);

add w to the waitQueue;

lock->Release();

w->P();

lock->Acquire();

delete w;

Condition::Signal(Lock* lock) {

Semaphore *w;

if anyone on waitQueue {

Take a waiting element off

and name it w;

w->V();

Approach

n Operating system level approach

n Create kernel threads

n Communicate priorities to kernel

n Use kernel’s communication and synchronization primitives

n But kernel threads are too expensive to create

n Solution: keep a pool of kernel threads, reuse within application

n Problems:

n Context switches are still slow

n Kernel keeps lot more state around and is not aware of user-level

program properties

n Cannot customize the scheduling policy

Approach

n One kernel thread for each processor in the system

n Implement user-level threads entirely at the user-level in

the runtime system

n Any user thread can run on any kernel thread

n Very fast thread creation and context switch

n Fast synchronization

n Can support much finer-grained parallelism

n Problem: Two schedulers!

n What if a task does blocking I/O

n Loses CPU

n What if there are other applications running on the machine?

n Application might lose a kernel thread at a “bad time”

n Application might sometimes need fewer threads

Scheduler Activations

n Mechanism of communicating between the two schedulers

n Scheduler activation:

n Vessel for running user threads (acts like a kernel thread)

n Can think of it as a virtual processor

n Notifies the user-level runtime system of interesting kernel events

n Provides space for saving processor context of the currently running

user thread when the thread is stopped in the kernel

n Old world: fixed # of kernel threads

n New world: fixed number of “running” threads

Scenario 1

n Application has certain number of activations running

n If an activation is blocked:

n New activation is created

n Allows the user-level scheduler to run in this new activation

n Runtime scheduler can schedule another user thread to run on the

new activation

X X

Scenario 4

n New CPU becomes available

n Kernel creates a new activation

n User-level scheduler picks a new thread to execute on the

new activation

n In addition, at any point the application can tell kernel it

doesn’t need extra CPUs

Summary

n Three key features about this approach:

n Goal is the get user-level threads performance with the scheduling

consistency provided by kernel-level threads in multiprocessors

n The problem to solve: coordinating two independent thread

schedulers

n Scheduler activations used to transmit information between the two

as well as to provide virtual processors

n Lesson: export your functionality (in this case, threads) out

of the kernel for improved performance and flexibility

n Figure out how to interact with the kernel “just enough”