cs 470 lecture 10 - university of...
TRANSCRIPT
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 1
Lecture 10
Reminder: Homework 2 due today. Homework 3 posted. Threads (Ch. 4) will be
covered on Monday. Questions?
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 2
Outline
Monitors Dining philosophers problem, again Implementing monitors Resource allocation problem
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 3
Monitors
Recall: a monitor is basically a class with built-in synchronization.
All data is private; only one entry function runs at any given time. I.e., ME to data is ensured.
monitor MonitorName{ // shared variable decls
// constructor inits vars MonitorName( ){...}
// entry member functions entry void P(...){...} entry void Q(...){...} ...}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 4
Condition Variables
While ME is ensured, this will not handle some synchronization situations. For example, it is possible to enter the monitor and decide that the state is not what is needed. Cannot stay in, but do not want to leave, either, for efficiency.
Introduce condition variables.
condition x, y;
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 5
Condition Variables
Condition variables have two operations also called wait and signal (but not exactly the same as the semaphore operations): x.wait( ) - suspend the process until a signal x.signal( ) - resume exactly one process; if none
are waiting this is a no-op
The goal is for x.signal( ) to be called when the condition the other processes are waiting for becomes true.
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 6
Condition Variables
entry void P(...){ ... if (<condition not met>) x.wait ( ); // condition should //be true ...}
entry void Q(...){ … if (<condition met>) x.signal( ); …}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 7
Dining Philosophers
To use a monitor to solve the Dining Philosopher's problem, we can have a condition variable for each philospher to wait on.
The main idea is that if a philosopher's neighbors are eating, then he must wait.
P0
P4
P1
P3
P2
RICE
C0
C4
C3
C2
C1
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 8
Dining Philosophers
In order to tell if a neighbor wants to eat, we also have a state variable for each philosopher. There are three possible states:
enum State = {thinking, hungry, eating};
where thinking means in the RS, eating means in the CS, and hungry means want to eat (i.e., in Entry section).
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 9
Dining Philosophers
The monitor definition starts out:monitor DiningPhilosophers { State state[5]; condition self[5];
DiningPhilosophers(){ for (int i=0; i<5; i++) state[i] = thinking; }
: :}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 10
Dining Philosophers
Philosopher k can transition from hungry to eating only when both neighbors are not eating:(state[(k+4)%5] != eating) // left ok && (state[(k+1)%5] != eating) // right ok
Note: the left neighbor has index k-1, but this may be a negative value, so for modulus arithmetic, -1 is the same as +(arrSize-1).
This test also is used by philosophers that are finished eating (i.e., in the Exit section) to determine if a neighbor should be allowed to eat. Need to check if neighbor is hungry.
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 11
Dining Philosophers
Wrap this into a utility (not entry) function:void Test (int k) { if (state[(k+4)%5] != eating) // left ok && (state[k] == hungry) // k hungry && (state[(k+1)%5] != eating)) // right ok { state[k] = eating; // allow k to eat self[k].signal(); // in case k is waiting }
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 12
Dining Philosophers
The processes interact with the monitor using two entry functions: PickUp - receives the index of the philosopher
process and attempts to enter the eating state. It waits if this is not currently possible.
entry void PickUp (int i) { state[i] = hungry; // show intention Test(i); // determine if can eat if (state[i] != eating) // still hungry self[i].wait(); // wait for neighbors}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 13
Dining Philosophers
PutDown - receives the index of the philosopher process. Sets philosopher state back to thinking and attempts to allow neighbors to eat.
entry void PutDown (int i) { state[i] = thinking;// done eating // let neighbors know Test((i+4)%5); // left Test((i+1)%5); // right}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 14
Dining Philosophers
To use the monitor, the philosopher processes share one DiningPhilosophers monitor:
shared DiningPhilosophers dp;
1. Loop 1.1. dp.PickUp(i) 1.2. Eat 1.3. dp.PutDown(i) 1.4. Think
As long as the condition variable queues are FIFO, you can show this solution meets the ME and progress criteria, but you can still have starvation.
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 15
Implementing Monitors
Just to show there is no magic here, can show how semaphores can be used to implement monitors (with waiting process going first when signaled).
Obviously, since only one process can execute inside a monitor at a time, need a mutex semaphore to control entry.
Processes that signal need to wait, but they are inside the monitor, so have another semaphore to control them. Call this one next.
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 16
Implementing Monitors
When a process leaves the monitor, it must wake up a waiting process if there is any. Priority goes to the processes already inside the monitor (waiting on next) and then the processes waiting to get in (waiting on mutex).
The variables we have so far are:
shared semaphore mutex = 1;shared semaphore next = 0;shared int nextCount = 0;
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 17
Implementing Monitors
Every entry function F becomes:
1. wait (mutex)2. original body of F3. if nextcount > 0 then 3.1 signal(next) // wake up one inside4. else 4.1 signal(mutex) // wake up one outside
For each condition variable, x, need a semaphore and counter, too, for its waiters.
shared semaphore xSem = 0;shared xCount = 0;
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 18
Implementing Monitors
When x.wait( ) executes, must wake up another process as before. Implement x.wait( ) as:
1. increment xCount2. if nextCount > 0 then 2.1 signal (next) // process inside3. else 3.1 signal (mutex) // process outside4. wait (xSem)5. decrement xCount
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 19
Implementing Monitors
When x.signal( ) executes, want the other process to go first, so put self on the next semaphore queue and wait for a turn:
1. if xCount > 0 then // others waiting 1.1. increment nextCount 1.2. signal (xSem) // wake up others 1.3. wait (next) // wait for next turn 1.4. decrement nextCount
Note: if no others waiting, just continue on, since x.signal( ) is a no-op in this case.
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 20
Example Execution
P0: P
1: P
2:
m.F0() m.F1() m.F2() wait(mutex) wait(mutex) wait(mutex)start F0 body : :: : :x.wait() : : incr xCount : : signal(mutex) : : wait(xSem) : :: start F1 body :: : :: x.signal() :: incr nextCount :: signal(xSem) :
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 21
Example Execution
P0: P
1: P
2:
: wait(next) : decr xCount : :: : :exit F0 body : : signal(next) : : decr nextCount : : : exit F1 body : signal(mutex) : start F2 body :
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 22
Priorities
We have assumed that the wait queues in a monitor are FIFO, but we do not always want this behavior. Can extend monitors to provide priorities to the waiters of a condition variable: x.wait(pri). Then the highest priority process waiting on x will be awakened on a signal.
This might be useful for a monitor that handles resource allocation:
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 23
Resource Allocator
monitor ResourceAllocator { bool busy; condition x; ResourceAllocator() { busy = false; }
entry void acquire (int burstSize) { if (busy) x.wait(burstSize); busy = true; }
entry void release() { busy = false; x.signal(); }}
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 24
Resource Allocator
This form requires that all processes observe the following protocol:
shared ResourceAllocator ra;
1. Loop 1.1. ra.aquire (t) 1.2. use resource - CS 1.3. ra.release( ) 1.4. RS
Wednesday, February 1 CS 470 Operating Systems - Lecture 10 25
Resource Allocator
Still cannot ensure all processes will follow the protocol, but it is easier to be correct than using bare semaphores or lower abstractions.
Note: putting the resource inside of a monitor will ensure ME access, but then the processes get scheduled according to the monitor's scheduling algorithm instead of the system's scheduling algorithm.