Semaphores and Other Wait-And-Signal Mechanisms
Total Page:16
File Type:pdf, Size:1020Kb
Semaphores and other Wait-and-Signal mechanisms Carsten Griwodz University of Oslo (including slides by Otto Anshus and Kai Li) Critical Regions Four conditions to provide mutual exclusion 1. No two threads simultaneously in critical region 2. No assumptions made about speeds or numbers of CPUs 3. No thread running outside its critical region may block another thread 4. No thread must wait forever to enter its critical region Critical Regions Mutual exclusion using critical regions Recall Processes and Threads • Process • Thread – Address space – Program counter – Program text and data – Registers – Open files – Stack – Child process IDs – Alarms – Signal handlers – Accounting information • Implemented in kernel or in user space • Implemented in kernel • Threads are the scheduled entities Producer-Consumer Problem • Main problem description – Two threads – Different actions in the critical region – The consumer can not enter the CR more often than the producer • Two sub-problems – Unbounded PCP: the producer can enter the CR as often as it wants – Bounded PCP: the producer can enter the CR only N times more often than the consumer Unbounded PCP Q PUT (msg) GET (buf) Rules for the queue Q: •No Get when empty Consumer Producer •Q shared, so must have mutex between Put and Get Recall Mutexes • Can be acquired and released – Only one thread can hold one mutex at a time – A second thread trying to acquire must wait • Mutexes – Can be implemented using busy waiting – Simpler with advanced atomic operations • Disable interrupts, TSL, XCHG, … – Still many approaches using busy waiting – Better implemented using system calls block & unblock Bounded PCP out B in Rules for the buffer B: Capacity: N •No Get when empty •No Put when full GET (buf) PUT (msg) •B shared, so must have mutex between Put and Get Producer Consumer Mutex Solution Put(msg) { Get(msg) { acquire(mutex); acquire(mutex); <put> while(empty) { release(mutex); release(mutex); Unbounded PCP } acquire(mutex); } <get> release(mutex); Busy waiting } Put(msg) { Get(msg) { acquire(mutex); acquire(mutex); while(full) { while(empty) { release(mutex); release(mutex); Bounded PCP acquire(mutex); acquire(mutex); } } <put> <get> release(mutex); release(mutex); } } Two Kinds of Synchronization LOCK is initially OPEN Acquire (id); Acquire (id); MUTEX <CR> <CR> Acquire will let first Release (id); Release (id); caller through, and then block next until Release LOCK is initially CLOSED CONDITION SYNCHRONIZATION Acquire (id); Release (id); Acquire will block first caller until Release SIGNAL Sleep and Wakeup / Signal and Wait • Wait (cond) • Signal (cond) – Insert(caller, – Unblock first in cond_queue) cond_queue, or just – Block this thread return if empty No counting, unused signals are ignored Wait is atomic Unbounded PCP using Signal and Wait Q Rules for the queue Q: •No Get when empty GET (buf): PUT (msg): •Q shared, so must have mutex between Put and Get Producer Consumer while(1) { while(1) { <process> if(empty) acquire(mutex); wait(cond); <insert> acquire(mutex); release(mutex); <remove> signal(cond); release(mutex); } <process> } Unbounded PCP using Signal and Wait Q GET (buf): PUT (msg): Producer Consumer while(1) { while(1) { <process> if(empty) acquire(mutex); wait(cond); <insert> acquire(mutex); release(mutex); <remove> signal(cond); release(mutex); } <process> } Lost signal Unbounded PCP using Signal and Wait Q GET (buf): PUT (msg): Producer Consumer while(1) { while(1) { <process> acquire(mutex); acquire(mutex); while(empty) { <insert> wait(cond); signal(cond); release(mutex); release(mutex); acquire(mutex); } Producer can’t enter } <remove> release(mutex); <process> } Unbounded PCP using Signal and Wait Q GET (buf): PUT (msg): Producer Consumer while(1) { while(1) { <process> acquire(mutex); acquire(mutex); while(empty) { <insert> release(mutex); signal(cond); wait(cond); release(mutex); acquire(mutex); } } <remove> Lost signal release(mutex); <process> } Threads wait for … • Access to a critical region – Mutex – Semaphore • A condition to be fulfilled – Condition variable – Barrier – Semaphore Semaphores Semaphores (Dijkstra, 1965) prolaag verhoog • Down or Wait or “P” • Up or Signal or “V” – Atomic – Atomic – Decrement semaphore – Increment semaphore by 1 value by 1 – Wake up a waiting thread if – Block if not positive any P(s) { V(s) { if (--s < 0) if (++s <= 0) Block(s); Unblock(s); } } Can get negative s: counts number of waiting threads s is NOT accessible through other means than calling P and V Semaphores w/Busy Wait P(s): V(s): while (s <= 0) {}; s++; s--; ATOMIC • Starvation possible? • Does it matter in practice? The Structure of a Semaphore s integer Threads waiting to get return after calling P (s) when s was <=0 sem_wait_queue V (s) P (s) Unblock Block (FIFO is fair) +1 -1 •Atomic: Disable interrupts •Atomic: P() and V() as System calls •Atomic: Entry-Exit protocols Using Semaphores “The Signal” “The Mutex” s := 0; s := 1; A B P (s); V (s); P (s); P (s); <CR> <CR> V(s); V(s); A blocks until B says V One thread gets in, next blocks until V is executed s := 11; “The Team” NB: remember to set the P(s); P(s); <max 11> <max 11> initial semaphore value! V(s); V(s); Up to 11 threads can pass P, the ninth will block until V is said by one of the eight already in there Simple to debug? A x := 0; B y := 0; P (x); P (y); ….. ….. V (y); V (x); What will happen? THEY ARE FOREVER WAITING FOR EACH OTHERS SIGNAL (“No milk”) Examples Unbounded PCP using Semaphores Q Rules for the queue Q: One semaphore for each condition we must wait •No Get when empty for to become TRUE: GET (buf): PUT (msg): •Q shared, so must have •Q empty: nonempty:=0; mutex between Put and Producer Consumer Get •Q mutex: mutex:=1; PUT (msg): GET (buf): •Is Mutex needed when only 1 P and 1 C? P(mutex); P(nonempty); •PUT at one end, GET at other end <insert> P(mutex); V(mutex); <remove> V(nonempty); V(mutex); Bounded PCP using Semaphores Rules for the buffer B: One semaphore for each out condition we must wait B •No Get when empty for to become TRUE: in •No Put when full •B empty: nonempty:=0; Capacity: N •B shared, so must have •B full: nonfull:=N mutex between Put and •B mutex: mutex:=1; GET (buf): Get PUT (msg): Producer Consumer PUT (msg): GET (buf): •PUT at one end, GET at other end P(nonfull); P(nonempty); P(mutex); P(mutex); <insert> <remove> V(mutex); V(mutex); V(nonempty); V(nonfull); Dining Philosophers Problem • Five philosopher • Five dishes • Five forks • But a philosopher needs two forks for eating • Usually the philosophers think, when they are hungry the try to eat • How to prevent all philosophers from starving Dining Philosophers s state •Each: 2 forks to eat • Free •5 philosophers: 10 forks to let all eat concurrently i+1 i i+1 •5 forks: 2 can eat concurrently i Mutex on whole table: P(mutex); T •1 can eat at a time eat; i V(mutex); Get L; Get R; P(s(i)); Ti •Deadlock possible P(s(i+1)); eat; V(s(i+1)); S(i) = 1 initially V(s(i)); Ti Get L; Get R if free else Put L; •Starvation possible Dining Philosophers To avoid starvation they could look after each other: s state •Entry: If L and R is not eating we can •Thinking •Eating •Exit: If L (R) wants to eat and L.L (R.R) is not i+1 eating we start him eating •Want i i+1 S() = 0 initially i P(mutex); state(i):=Want; if (state(i-1) !=Eating AND state(i+1) != Eating) Ti {/*Safe to eat*/ While (1) { state(i):=Eating; V(s(i)); /*Because */ } <think> V(mutex); ENTRY; P(s(i)); /* Init was 0!! <eat> We or neighbor must say V(i) to us!*/ EXIT; } P(mutex); state(i):=Thinking; if (state(i-1)=Want AND state(i-2) !=Eating) { state(i-1):=Eating; V(s(i-1)); /*Start Left neighbor*/ } /*Analogue for Right neighbor*/ V(mutex); Dining Philosophers s S(i) = 1 initially i+1 T1, T2, T3, T4: i i+1 P(s(i)): i •Remove the danger of P(s(i+1)); circular waiting (deadlock) <eat> V(s(i+1)); Can we in a simple way do better •T1-T4: Get L; Get R; than this one? V(s(i)); •T5: Get R; Get L; Get L; Get R; P(s(i)); •Deadlock possible P(s(i+1)); T5 eat; P(s(1)); V(s(i+1)); P(s(5)); V(s(i)); <eat> V(s(5)); V(s((1)); •Non-symmetric solution. Still quite elegant Readers and Writers Problem • Several threads • Shared data in a critical region • Sometimes a thread wants to read the data • Sometimes a thread wants to change the data • Readers can enter a critical region together • Writers can not enter a critical region together The Readers and Writers Problem P(mutex); rc = rc+1; if(rc==1) P(db); V(mutex); <read data> while(1) { P(mutex); <do things> rc = rc-1; if(you_want) if(rc==0) V(db); READ; V(mutex); else WRITE; } P(db); <write data> V(db); One solution to the readers and writers problem But too many readers can starve writers The Readers and Writers Problem P(stopreaders); P(mutex); rc = rc+1; if(rc==1) P(db); V(mutex); V(stopreaders); while(1) { <read data> <do things> P(mutex); if(you_want) rc = rc-1; READ; if(rc==0) V(db); else V(mutex); WRITE; P(stopreaders); } P(db); V(stopreaders); <write data> V(db); Another solution to the readers and writers problem Other wait-and-signal mechanisms Event Count (Reed 1977) • Init( ec ) – Set the eventcount to 0 • Read( ec ) – Return the value of eventcount ec • Advance( ec ) – Atomically increment ec by 1 • Await( ec, v ) – Wait until ec >= v Bounded PCP with Event Count out in=out=0; B producer() { consumer() { in int next = 0; int next = 0; Capacity: N while (1) { while ( 1 ) { produce an item next++; next++; await(in, next); await(out, next - N); take an item from buffer; put the item in buffer; advance(out); advance(in);