210 likes | 324 Views
CS 162 Discussion Section Week 2 (2/6 - 2/7). Today’s Section. Project Administrivia (5 min) Quiz (5 min) Review Lectures ( 10 min) Worksheet and Discussion ( 30 min). Design Documents. Overview of the project as a whole along with each of its subparts
E N D
Today’s Section • Project Administrivia (5 min) • Quiz (5 min) • Review Lectures(10 min) • Worksheet and Discussion (30min)
Design Documents • Overview of the project as a whole along with each of its subparts • Header must contain the following info • Project Name and # • Group Members Names and IDs • Section # • TA Name • Example doc on PIAZZA!
Design Document Structure Each part of the project should be explained using the following structure • Overview • Correctness Constraints • Declarations • Descriptions • Testing Plan
Design Doc Length • Keep under 15 pages • Will dock points if too long!
Dispatch Loop • Conceptually, the dispatching loop of the operating system looks as follows: Loop { RunThread(); ChooseNextThread(); SaveStateOfCPU(curTCB); LoadStateOfCPU(newTCB); } • This is an infinite loop • One could argue that this is all that the OS does
Yielding through Internal Events • Blocking on I/O • The act of requesting I/O implicitly yields the CPU • Waiting on a “signal” from other thread • Thread asks to wait and thus yields the CPU • Thread executes a yield() • Thread volunteers to give up CPU computePI() { while(TRUE) { ComputeNextDigit(); yield(); } } • Note that yield() must be called by programmer frequently enough!
Review: Two Thread Yield Example • Consider the following code blocks: proc A() { B(); } proc B() { while(TRUE) { yield(); } } • Suppose we have two threads: • Threads S and T Thread T Thread S A A B(while) B(while) yield yield kernel_yield kernel_yield run_new_thread run_new_thread switch switch
Why allow cooperating threads? • People cooperate; computers help/enhance people’s lives, so computers must cooperate • By analogy, the non-reproducibility/non-determinism of people is a notable problem for “carefully laid plans” • Advantage 1: Share resources • One computer, many users • One bank balance, many ATMs • What if ATMs were only updated at night? • Embedded systems (robot control: coordinate arm & hand) • Advantage 2: Speedup • Overlap I/O and computation • Multiprocessors – chop up program into parallel pieces • Advantage 3: Modularity • Chop large problem up into simpler pieces • To compile, for instance, gcc calls cpp | cc1 | cc2 | as | ld • Makes system easier to extend
Definitions • Synchronization: using atomic operations to ensure cooperation between threads • For now, only loads and stores are atomic • We’ll show that is hard to build anything useful with only reads and writes • Critical Section: piece of code that only one thread can execute at once • Mutual Exclusion: ensuring that only one thread executes critical section • One thread excludes the other while doing its task • Critical section and mutual exclusion are two ways of describing the same thing
Better Implementation of Locks by Disabling Interrupts • Key idea: maintain a lock variable and impose mutual exclusion only during operations on that variable int value = FREE; Acquire() {disable interrupts; if (value == BUSY) { put thread on wait queue; Go to sleep(); // Enable interrupts? } else {value = BUSY;}enable interrupts; } Release() {disable interrupts; if (anyone on wait queue) { take thread off wait queue; Put at front of ready queue; } else {value = FREE; }enable interrupts; }
contextswitch contextswitch How to Re-enable After Sleep()? • Since ints are disabled when you call sleep: • Responsibility of the next thread to re-enable ints • When the sleeping thread wakes up, returns to acquire and re-enables interrupts Thread AThread B . . disable ints sleep . . sleep return enable ints . . yield returnenable ints disable int yield
Implementing Locks with test&set • Simple solution: int value = 0; // Free Acquire() { while (test&set(value)); // while busy } Release() { value = 0; } • Simple explanation: • If lock is free, test&set reads 0 and sets value=1, so lock is now busy. It returns 0 so while exits • If lock is busy, test&set reads 1 and sets value=1 (no change). It returns 1, so while loop continues • When we set value = 0, someone else can get lock test&set (&address) { result = M[address]; M[address] = 1; return result;}
Problem: Busy-Waiting for Lock • Positives for this solution • Machine can receive interrupts • User code can use this lock • Works on a multiprocessor • Negatives • Inefficient: busy-waiting thread will consume cycles waiting • Waiting thread may take cycles away from thread holding lock! • Priority Inversion: If busy-waiting thread has higher priority than thread holding lock no progress! • Priority Inversion problem with original Martian rover • For semaphores and monitors, waiting thread may wait for an arbitrary length of time! • Even if OK for locks, definitely not ok for other primitives • Homework/exam solutions should not have busy-waiting!
int guard = 0; int value = FREE; Acquire() { // Short busy-wait timewhile (test&set(guard)); if (value == BUSY) { put thread on wait queue; go to sleep() & guard = 0; } else {value = BUSY;guard = 0; }} Better Locks using test&set • Can we build test&set locks without busy-waiting? • Can’t entirely, but can minimize! • Idea: only busy-wait to atomically check lock value • Note: sleep has to be sure to reset the guard variable • Why can’t we do it just before or just after the sleep? Release() { // Short busy-wait timewhile (test&set(guard)); if anyone on wait queue { take thread off wait queue Place on ready queue; } else {value = FREE; }guard = 0;
Two Uses of Semaphores • Mutual Exclusion (initial value = 1) • Also called “Binary Semaphore”. • Can be used for mutual exclusion: semaphore.P(); // Critical section goes here semaphore.V(); • Scheduling Constraints (initial value = 0) • Allow thread 1 to wait for a signal from thread 2, i.e., thread 2 schedules thread 1 when a given constrained is satisfied • Example: suppose you had to implement ThreadJoin which must wait for thread to terminiate: Initial value of semaphore = 0 ThreadJoin { semaphore.P(); } ThreadFinish { semaphore.V(); }
Motivation for Monitors and Condition Variables • Cleaner idea: Use locks for mutual exclusion and condition variables for scheduling constraints • Monitor: a lock and zero or more condition variables for managing concurrent access to shared data • Some languages like Java provide this natively • Most others use actual locks and condition variables
Hoare monitors • Signaler gives up lock, CPU to waiter; waiter runs immediately • Waiter gives up lock, processor back to signaler when it exits critical section or if it waits again • Most textbooks … lock.Acquire() … dataready.signal(); … lock.Release(); Lock.Acquire() … if (queue.isEmpty()) { dataready.wait(&lock); }… lock.Release(); Lock, CPU Lock, CPU
Mesa monitors • Signaler keeps lock and processor • Waiter placed on a local “e” queue for the monitor • Practically, need to check condition again after wait • Most real operating systems Put waiting thread on ready queue … lock.Acquire() … dataready.signal(); … lock.Release(); Lock.Acquire() … while (queue.isEmpty()) { dataready.wait(&lock); }… lock.Release(); schedule waiting thread