Background

43
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? Consumer Consumer Producer Producer

description

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?. Producer. Consumer. Race Condition. A Condition where the outcome depends on execution order. - PowerPoint PPT Presentation

Transcript of Background

Page 1: 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

Page 2: Background

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

Page 3: Background

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

Page 4: Background

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

Page 5: Background

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

Page 6: Background

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

Page 7: Background

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();

Page 8: Background

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

Page 9: Background

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)}

}

Page 10: Background

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?

Page 11: Background

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();} }

Page 12: Background

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

Page 13: Background

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

Page 14: Background

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

Page 15: Background

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());}}}

Page 16: Background

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

Page 17: Background

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.

Page 18: Background

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?

Page 19: Background

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

Page 20: Background

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

Page 21: Background

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

Page 22: Background

Generic Monitor Syntax

Page 23: Background

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.

Page 24: Background

• 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

Page 25: Background

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}

Page 26: Background

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.

Page 27: Background

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();}

Page 28: Background

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(); }}}

Page 29: Background

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.

Page 30: Background

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

Page 31: Background

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;} }

Page 32: Background

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(); }}

Page 33: Background

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

Page 34: Background

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.

Page 35: Background

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

Page 36: Background

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)

Page 37: Background

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

Page 38: Background

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

Page 39: Background

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

Page 40: Background

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.

Page 41: Background

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

Page 42: Background

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

Page 43: Background

Schedule Possible Under Timestamp Protocol