Background
-
Upload
kyle-middleton -
Category
Documents
-
view
16 -
download
1
description
Transcript of Background
Background
• Concurrent access to shared data can lead to inconsistencies
• Maintaining data consistency among cooperating processes is critical
• What is wrong with the code to the right?
ConsumerConsumer
ProducerProducer
Race Condition
• count++, count-- may not use one machine instruction– register1=count, register1=register1+1, count=register1– register2=count, register2=register2–1, count=register2
• Suppose: P= producer, C= consumer, and count = 5– P: register = count // 5– P: register = register + 1 //6– C: register = count //5– C: register = register – 1 //4– P: count = register //6– C: count = register //4
• Questions– What is the final value for count?– What other possibilities?– What should be the correct value?
A Condition where the outcome depends on execution order
Critical Sections
We cannot assume• Process execution
speed, although we can assume they all execute at some non zero speed
• Which process executes next
• Process execution order
A block of instructions that must be protected from concurrent execution
Race Condition: Concurrent access to shared data where the output depends on the order of execution
Critical-Section Solutions
1. Mutual Exclusion – There is a mechanism to limit (normally one) of processes that can execute in a critical section at any time
2. Progress - If no process is executing in its critical section, processes waiting to enter cannot wait definitely
3. Bounded Waiting - A process cannot be preempted by other processes from entering a critical section an infinite number of times.
We need a protocol to guarantee the following:
No assumptions can be made regarding process speed or scheduling order
Peterson's Two Process Solution
• Assume atomic instructions
• Processes share:– int turn; – Boolean flag[2]
• turn: indicates whose turn it is.
• flag – indicates if a process is
ready to enter– flag[i] = true implies that
process Pi is ready!
Does Peterson satisfy mutual exclusion, progress, bounded wait?
Spinning or 'busy wait' is when there is no blocking
Hardware Solutions• Hardware atomic instructions
– test and set– swap– increment and test if zero
• Disable interrupts– Could involve loss of data– Won't work on multiprocessors without
inefficient message passing
Atomic operation: One that cannot be interrupted. A pending interrupt will not occur till the instruction completes
Simulate a Hardware Solutionpublic class HardwareData
{ private boolean value = false;
public HardwareData(boolean value) { this.value = value; }
public void release(boolean vaue) { this.value = value; }
public synchronized boolean lock() { return value; }
public synchronized boolean getAndSet(boolean value)
{ boolean old = value; this.value = value; return old; }
public synchronized void swap(HardwareData other)
{ boolean old = value; value = other.value; other.value = value; }
}
Usage: HardwareData hdware = new HardwareData(false); // Threads shareHardwareData hdware2 = new HardwareData(true);
Either: while (hdware.getAndSet(true)) Thread.yield();Or: hdware.swap(hdware2); while (hdware2.lock() == true) ; // Wait - Spin
criticalSection(); hdware.release(false); someOtherCode();
Operating System Synchronization• Uniprocessors – Disable interrupts
– Currently running code would execute without preemption– The executing code must be extremely quick– Not scalable to multiprocessor systems, where each
processor has its own interrupt vector
• Multiprocessor - atomic hardware instructions– Spin locks are used; must be extremely quick– Locks that require significant processing use other
mechanisms: preemptible semaphores
Semaphores• Synchronization tool that
uses blocks• A Semaphore is an
abstract data type– Contains an integer
variable– Atomic acquire method
(P – proberin)– Atomic release method
(V – verhogen)
• Includes an implied queue (or some randomly accessed data structure)
acquire(){ value--;
if (value<0){ add P to list block
} }
release(){ value++;
if (value <=0){ remove P from list wakeup(P)}
}
Semaphore for General Synchronization
Semaphore S = new Semaphore(n);
sem.acquire()
// critical section code
sem.release()
• Counting semaphore – the integer value has any range
– can count available resources
• Binary semaphore – integer value: 0 or 1
– Also known as mutex locks
• Semaphores are error prone– acquire instead of release
– forget to acquire or release
Advantages and disadvantages?
Java Semaphores (Java 1.5)public class Worker implements Runnable{ private Semaphore sem; public Worker(Semaphore sem) { this.sem = sem; } public void run() { while (true)
{ sem.acquire(); doSomething(); sem.release(); doMore(); }
}
public class Factory{ public static void main(String[] args) { Semaphore sem = new Semaphore(1); Thread[] bees = new Thread[5]; for (int i=0; i<5; i++) bees[i] = new Worker(sem);
for (int i=0; i<5; i++) bees[i].start();} }
Semaphore Implementation
• It is possible that a semaphore needs to block while holding a mutex– Issue a wait() call to release the mutex– Another process must wake up the waiting
thread (notify() or notifyAll()). Otherwise the thread will never execute.
• Good design will minimize the time spent in critical sections
Deadlock and Starvation• Deadlock – two or more processes wait for a resource that
can never be satisfied.
• Let S and Q be two semaphores initialized to 1 P0 P1P0 P1 S.acquire(); Q.acquire(); Q.acquire(); S.acquire(); // Critical Section // Critical Section S.release(); Q.release(); Q.release(); S.release();
• Starvation: Continual preemption (indefinite blocking)– Order of servicing the blocking list (LIFO for example)– Process priorities prevent a process from exiting a
semaphore queue
The Bounded Buffer problem
• One or more producers add elements to a buffer• One or more consumers extract produced
elements from the buffer for service• There are N of buffers (hence a bounded
number)• Solution with semaphores
– Three semaphores, mutex, full, and empty– Initialize mutex to 1, full to 0 and empty to N– The full semaphore counts up, and the empty
semaphore counts down
The Bounded Buffer Solutionpublic class BoundedBufferExample{ public static void main(String args[]) { BoundedBuffer buffer = new BoundedBuffer(); new Producer(buffer).start(); new Consumer(buffer).start(); } }public class Producer extends Thread{ private BoundedBuffer buf; public Producer(Buffer buf) {this.buf=buf;} public void run() { while (true) { sleep(100); buffer.insert(new Date()); } } }public class Consumer extends Thread{ private BoundedBuffer buf; public Consumer(Buffer buf) {this.buf=buffer;} public void run() { while (true) { sleep(100); System.out.println( (Date)buffer.remove());}}}
Bounded Buffer Semaphore Solution public class BoundedBuffer { private static final int SIZE = 5; private Object[] buffer; private int in, out; private Semaphore
mutex, empty, full;
public BoundedBuffer() { in = out = 0; buffer = new Object[SIZE]; mutex = new Semaphore(1); empty = new Semaphore(SIZE); full = new Semaphore(0); } }
public void insert(Object item){ empty.acquire(); mutex.acquire(); buffer[in] = item; in = (in+1)%SIZE; mutex.release(); full.release();}
public Object remove() { full.acquire(); mutex.acquire(); Object item = buffer[out]; out = (out+1)%SIZE; mutex.release(); empty.release(); return item;}
Demonstrates binary andCounting semaphores
The Readers-Writers Problem• Data is shared among concurrent processes
– Readers: only read the data set, without any updates– Writers: May both read and write.
• Problem – Multiple readers can read at concurrently– Only one writer can concurrently access shared data
• Shared Data– Data set– Semaphore mutex initialized to 1.– Semaphore db initialized to 1 // Acquired by the first reader– Integer readerCount initialized to 0 counting upwards.
Note: The best solution is to disallow more readers while a writer waits. Otherwise, starvation is possible.
Reader Writers with Semaphoresclass DataBase { private int readers; private Semaphore mutex, db; public DataBase() { readers = 0; mutex = new Semaphore(1); db = new Semaphore(1); } public void acquireRead() // First reader acquired db { mutex.acquire(); if (++readers==1) db.acquire();
mutex.release(); } public void releaseRead() // Last reader released db { mutex.acquire(); if (--readers==0) db.release(); mutex.release(); } public void acquireWrite() {db.acquire();} public void releaseWrite() {db.release();}}
How would we disallow more readers after a write request?
Reader Writer User Classesclass Reader extends Thread { private DataBase db; public Reader(DataBase db) {this.db = db;} public void run() { while(true) { sleep(500);db.acquireRead();
doRead(); db.releaseRead();} } }
class Writer extends Thread{ private Locks db; public Writer(DataBase db) {this.db = db;} public void run() { while (true) { sleep(500); db.acquireWrite();
doWrite(); db.releaseWrite();}} } Hints for the lab project
1. make an array of DataBase objects2. add throws InterruptedException to the methods3. Randomly choose the sleep length
Condition Variables
• Condition x;• Two operations on a condition • x.wait ()
– Block the process invoking this operation– Expects a subsequent signal call by another process
• x.signal () – Wake up a process blocked because of wait()– If no processes are blocked, ignore– Which process wakes up? Answer: indeterminate
An object with wait and signal capability
Note: wait and signal normally execute after some condition occurs
Monitors
Monitor without condition variables Monitor with condition variables
A high-level abstraction integrated into the syntax of the languageOnly one process may be active within the monitor at a time
Generic Monitor Syntax
Java Monitors• Every object has a single monitor lock• A call to a synchronized method
– Lock Available: acquires the lock– Lock Not available: block and wait in the entry set
• Non synchronized methods ignore the lock• Lock released when a synchronized method returns• The entry queue algorithm varies (normally FCFS)• Recursive locking occurs if a method with the lock calls
another synchronized method in the object. This is legal.
• wait(): block the process and move to the wait set.• notify()
– An arbitrary thread from the wait set moves to the entry set.– A thread not waiting for that condition reissues a wait()
• notifyAll() – All threads in the wait set move to the entry set.– Threads not waiting for that condition call wait again
• Notes:– notify() & notifyAll() are ignored if the wait() set is empty– wait() & notify() are single condition variables per Java monitors– Java 1.5 adds additional condition variable support– Calls to wait() and notify() are legal only when lock is owned
Java Synchronization
Block Synchronization
• Scope– time between acquire
and release– synchronizing blocks
of code in a method can reduce the scope
• How to do it– Instantiate a lock object– Use the synchronized
keyword around a block– Use wait() and notify()
calls as needed
Acquiring an object's lock from outside a synchronized method
Example
Object lock=new Object();synchronized(lock){ // somecritical code. lock.wait();
// more criticalcode lock.notifyAll();
}
Question: What's wrong with:
synchronized(new Object()) {// code here}
New Java Concurrency Features
• Problem in old versions of Java– notify(), notifyAll(), and wait() are a single condition
variable for an entire class. – This is not sufficient for every application.
• Java 1.5 solution: condition variables
Lock key = new ReentrantLock();Condition condVar = key.newCondition();
• Once created, a thread can use the await() and signal() methods.
Dining-Philosophers Problem
Shared data
– Bowl of rice (data set)– Semaphore chopStick
[5] initialized to 1
Philosopher i loopPhilosopher i loop
while (true){ sleep((int)Math.random()*TIME); chopStick[i].acquire(); chopStick[(i+1)%5].acquire(); eat(); chopStick[i].release(); chopStick[(i+1)%5].release; think();}
Dining Philosophers (Condition variables)
class DiningPhilosophers{ enum State {THINK, HUNGRY, EAT}; Condition[] self = new Condition[5]; State[] state = new State[5];
public DiningPhilosophers{ Lock lock = new ReentrantLock();
for (int i=0;i<5;i++) { state[i]=State.THINK; Condition[i]=lock.newCondition(); }}
public void takeForks(int i){ state[i] = state.HUNGRY; test(i) while (state[i] != State.EAT) self[i].await(); }
public void returnFork(int i){ state[i] = State.THINK; test((i+1)%5); test((i+4)%5); }
private void test(int i){ if((state[(i+4)%5]!=State.EAT)&&(state[i]==State.HUNGRY)
&& (state[(i+1)%5]!=State.EAT)) { state[i] = State.EAT; self[i].signal(); }}}
Dining Philosophers with Java Monitor
class Chopsticks{ boolean[] stick = new boolean[5]; public synchronized Chopsticks() { for (int i=0;i<5;i++) stick[i]=false; } public synchronized void pickUp(int i) { while (st[i]) wait(); stick[i]=true; while (stick[(i+1)%5]) wait(); stick[(i+1])%5]=true; } public synchronized void putDown(int
i) { stick[i] = false;
stick[(i+1)%5] = false; notifyAll(); }
}
Void run(){ String me = thread.currentThread.getName(); int stick = Integer.parseInt(me); while (true) { chopStick.pickUp(stick); eat(); chopStick.putDown(stick); think(); }
Note: All philosopher threads mustshare the Chopsticks object.
Bounded Buffer - Java
public synchronized void insert(Object item){ while (count==SIZE) Thread.yield(); buffer[in]=item; in=(in+1)%SIZE; ++count;}
public synchronized Object remove(){ while (count==0) Thread.yield(); Object item = buffer[out]; out = (out+1)%SIZE; --count; return item;}
What bugs are here?
Synchronized insert() and remove() methods
Java Bounded Buffer with Monitors
public class BoundedBuffer{ private int count, in, out; private Object buf; public BoundedBuffer(int size) { count = in = out = 0; buf = new Object[size]; } public synchronized void insert(Object item) throws InterruptedException { while (count==buffer.length) wait(); buf[in] = item; in = (in + 1)%buf.length; ++count; notifyAll(); } public synchronized Object remove() throws InterruptedException { while (count==0) wait(); Object item = buf[out]; out=(out+1)%buf.length; -- count; notifyAll(); return item;} }
Java Readers-Writers with Monitors
public class Database{ private int readers; private boolean writers; public Database() {readers=0; writers=false; } public synchronized void acquireRead() throws InterruptedException { while (writers) wait(); ++readers; } public synchronized void releaseRead() { if (--readers==0) notify(); } public synchronized void acquireWrite() throws InterruptedException { while (readers>0||writers) wait(); writers=true;} public synchronized void releaseWrite() { writers=false; notifyAll(); }}
Solaris SynchronizationA variety of locks are available
• Adaptive mutexes– Spin while waiting for lock if the holding task is executing– Block if the holding task is blocked– Note: Always block on a single processor machines
because only one task can actually execute.
• condition variables and readers-writers locks– Threads block if locks are not available
• Turnstiles– Threads block on a FCFS queue as locks awarded
Windows XP and Linux• Kernel locks
– Single processor
• Windows: Interrupt masks allow high priority interrupts
• Linux: Disables interrupts for short critical sections
– Multiprocessor: Spin locks
• User locks
– Windows: Dispatcher object handles; callback mechanism
– Linux: semaphores and spin locks
• P-threads: OS independent API
– Standard: mutex locks and condition variables
– Non universal extensions: reader-writer, and spin locks
Note: A Windows dispatcher object is a low-level synchronization module that widows uses to control user locks, semaphores, callback events, timers, and inter-process communication.
Transactions
• Requirements– Perform in its entirety, or not at all– Assure atomicity– Stable Storage: Operate during failures (typically
RAID – Redundant Array of Inexpensive disks)
• Transactions consist of:– A series of read and write operations– A commit operation completes the transaction– An abort operation cancels (rolls back) the any
changes performed
A collection of operations performed as an atomic unit
Stable storage: a group of disks that mirror every operation
Write-ahead logging
• The Log– Transaction name, item name, old & new values– Writes to stable storage before data operations
• Operations– <Ti starts> when transaction Ti starts– <Ti commits> when Ti commits
• Recovery methods (Undo(Ti), and Redo(Ti) )– restore or set values of all data affected by T i
– must be idempotent (same results if repeated)
• System uses the log to restore state after failures– log: <Ti starts> without <Ti commits>: undo(Ti)– log: <Ti starts> and <Ti commits>, redo(Ti)
Checkpoints
• Purpose: shorten long recoveries• Checkpoint scheme:
– flush records periodically to stable storage.– Output a <checkpoint> record to the log
• Recovery– Only consider transactions, Ti, that started
before the most recent checkpoint but hasn’t committed, or transactions starting after Ti
– All other transactions already on stable storage
Serial Schedule
• Transactions require multiple reads and writes
• If T0 & T1 are transactions
– T0,T1, & T1,T0 are serial schedules
• T0, T1, & T2 are transactions
– T0,T1,T2,, T0,T2,T1, T1,T0,T2 , T1,T2,T0, T2,T0,T1, T2,T1,T0, are serial schedules
A serial schedule is a possible atomic order of execution
Note: N transactions (Ti) imply N! serial schedules
Concurrency-control algorithms: those that ensure a serial scheduleNaïve approach: Single mutex controlling all transactions. However, this is inefficient because transactions seldom access the same data
Non-serial Schedule• A non-serial schedule allows
reads and writes from one transaction occur concurrently with those from another
• It's a Conflict if transactions access the same data item with at least one write.
• If an equivalent serial schedule exists, to one where reads/writes overlap; it is then conflict serializable
The above operations are conflict serializable
Pessimistic Locking Protocol• Transactions acquire reader/writer locks in advance. If a
lock is already held, the transaction must block
• Two-phase protocol1. Each transaction issues lock and unlock requests in two
phases2. During the growing phase, a transaction can obtain locks.
It cannot release any.3. During the release phase, a transaction can release
locks in a shrinking phase. It cannot obtain any
• Problems– Deadlock can occur– Locks typically get held for too long
Note: Reader/writer locks are called shared/exclusive locks in transaction processing. They apply to particular data items.
Optimistic Locking Protocol• Goal: Not to prevent, but detect conflicts
• Mechanism: Numerically time stamp each transaction with an increasing transaction number written to items when reading or writing
• Conflict occurs when– We read a transaction that was written by a transaction with
a future time stamp– If we are about to write a transaction read or wrtiten by a
future transaction with a previous timestamp
• Action: Roll back, get another timestamp, try again
• Advantage: minimizes lock time and is deadlock free• Disadvantage: many roll backs if lots of contention
Time Stamp Implementation• Each data item has two time stamps: The largest successful
write and the largest successful read
• Read a data record– If Ti timestamp < item write timestamp, we cannot read a value
written in the future. A roll back is necessary
– If Ti timestamp > item write timestamp, perform read and update the item read timestamp
• Write a data record– If Ti < item read timestamp, we cannot write over an item read in
the future. A roll back is necessary– IF Ti < data write timestamp, we cannot write over a record
written in the future. A roll back is necessary– Otherwise, the write is successful
• Implementation Issues: Time stamp assignments must be atomic. Each read and write must be atomic
Schedule Possible Under Timestamp Protocol