COS 226_03 - Acknowledgement COS 226 Chapter 3 Concurrent...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Acknowledgement COS 226 Chapter 3 Concurrent objects Some of the slides are taken from the companion slides for “The Art of Multiprocessor Programming” by Maurice Herlihy & Nir Shavit Objectivism What is a concurrent object? How do we describe one? How do we implement one? How do we tell if we’re right? FIFO queue A Lock-Based Queue LockBasedQueue<T> class LockBasedQueue<T> { int head, tail; T items; lock; Lock lock; public LockBasedQueue(int capacity) { head = 0; tail = 0; ReentrantLock(); lock = new ReentrantLock(); Object[capacity]; items = (T) new Object[capacity]; } A Lock-Based Queue LockBasedQueue<T> class LockBasedQueue<T> { int int head, tail; T items; lock; Lock lock; public public LockBasedQueue(int capacity) { head = 0; tail = 0; ReentrantLock(); lock = new ReentrantLock(); Object[capacity]; items = (T) new Object[capacity]; } Queue fields protected by single shared lock 1 A Lock-Based Queue LockBasedQueue<T> class LockBasedQueue<T> { int head, tail; T items; lock; Lock lock; public public LockBasedQueue(int capacity) { head = 0; tail = 0; new ReentrantLock(); lock = new ReentrantLock(); new Object[capacity]; items = (T) new Object[capacity]; } A Lock-Based Queue public void enq(T x) throws FullException { lock.lock(); lock.lock(); try { items.length) if (tail – head == items.length) FullException(); throw new FullException(); items[tail] items.length] items[tail] % items.length] = x; tail++; } finally { lock.unlock(); lock.unlock(); Method calls } mutually exclusive } Initially head = tail Implementation: Deq deq() public T deq() throws EmptyException { lock.lock(); lock.lock(); try { if (tail == head) EmptyException(); throw new EmptyException(); T x = items[head % items.length]; items.length]; head++; return x; } finally { Method calls lock.unlock(); lock.unlock(); mutually exclusive } } Now consider the following implementation The same thing without mutual exclusion For simplicity, only two threads One thread enq only The other deq only Wait-free 2-Thread Queue public class WaitFreeQueue { int head = 0, tail = 0; Object[capacity]; items = (T) new Object[capacity]; public public void enq(Item x) { (tail// busywhile (tail-head == capacity); // busy-wait items[tail % capacity] = x; tail++; } ct” orre deq() public Item deq() { e “c re not fin // busywhile (tail == head); // ebusy-wait e d tions a Item item = items[head o%wcapacity]; head++; a w d odific sive? return item; Ho clu nm }} whe lly ex Defining concurrent queue implementations Need a way to specify a concurrent queue object Need a way to prove that an algorithm implements the object’s specification Lets talk about object specifications … ua mut 2 Correctness and Progress In a concurrent setting, we need to specify both the safety and the liveness properties of an object Need a way to define when an implementation is correct the conditions under which it guarantees progress Lets begin with correctness Sequential Objects Each object has a state Usually given by a set of fields Queue example: sequence of items Each object has a set of methods Only way to manipulate state Queue example: enq and deq methods enq deq Sequential Specifications If (precondition) the object is in such-and-such a state before you call the method, Then (postcondition) the method will return a particular value or throw a particular exception. and (postcondition, con’t) the object will be in some other state when the method returns, Pre and PostConditions for Dequeue Precondition: Queue is non-empty Postcondition: Returns first item in queue Postcondition: Removes first item in queue Pre and PostConditions for Dequeue Precondition: Queue is empty Why Sequential Specifications Totally Rock Interactions among methods captured by sideeffects on object state State meaningful between method calls Postcondition: Throws Empty exception Documentation size linear in number of methods Each method described in isolation Postcondition: Queue state unchanged Can add new methods Without changing descriptions of old methods 3 What About Concurrent Specifications ? Methods? Documentation? Adding new methods? Methods Take Time invocation 12:00 q.enq( q.enq(...) response 12:01 void Method call time Sequential vs Concurrent Sequential Method calls take time? Who knew? Concurrent Methods Take Overlapping Time Concurrent Method call is not an event Method call is an interval. Starts with invocation event Ends with response event Method is pending if invocation has occurred but not yet response time Concurrent Methods Take Overlapping Time Concurrent Methods Take Overlapping Time Method call Method call Method call time time 4 Concurrent Methods Take Overlapping Time Sequential vs Concurrent Sequential: Object needs meaningful state only between method calls Method call Method call time Method call Concurrent Because method calls overlap, object might never be between method calls Sequential vs Concurrent Sequential: Each method described in isolation Sequential vs Concurrent Sequential: Can add new methods without affecting older methods Concurrent Must characterize all possible interactions with concurrent calls What if two enqs overlap? Two deqs? enq and deq? … Concurrent: Everything can potentially interact with ic! P an everything else The Big Question What does it mean for a concurrent object to be correct? What is a concurrent FIFO queue? FIFO means strict temporal order Concurrent means ambiguous temporal order Read-write example Two threads concurrently write -3 and 7 to a register Later when another thread accesses the register it returns -7 Clearly this is wrong – we expect either -3 or 7, but not a mixture 5 Principle 3.3.1 Method calls should appear to happen in a one-at-a-time sequential order Principle 3.3.2 Method calls separated by a period of quiescence should appear to take effect in real-time order In other words, method calls who are separated by a period of inactivity should appear in the order of their appearance Suppose A and B concurrently enqueue x and y, C then enqueues z. We may not be able to predict the order of x and y, but we know they are ahead of z Quiescent consistency Together principle 3.3.1 and 3.3.2 form a correctness property: Quiescent consistency Quiescent consistency An object is quiescent consistent if: Its method calls appear to be in a sequential order Its method calls take place in a real-time order if separated by a period of inactivity Quiescent consistency A quiescently consistent shared counter would Return numbers, not necessarily in the order of the requests, but Will not duplicate or omit a number Quiescent consistency Quiescent consistency is compositional since if each object in the system is quiescent consistent, the whole system will be quiescent consistent. 6 Another read-write example A single thread writes 7 and then -3 to a shared register Later it reads the register and returns 7 This is also not acceptable since the value it read is not the last value it wrote Principle 3.4.1 Method calls should take effect in program order Program order – The order in which a single thread issues method calls Sequential consistency Together principles 3.3.1 and 3.4.1 form a second correctness property: Sequential consistency Sequential consistency An object is sequential consistent if: Its method calls are in a sequential order Its method calls are in program order Sequential consistency In any concurrent execution, there is a way to order the method calls sequentially so that They are consistent with program order They meet the object’s sequential specifications Sequential consistency A.enq(x) concurrent with B.enq(y), then A.deq(y) concurrent with B.deq(x) Two possible sequential orders: A.enq(x) B.enq(y) B.deq(x) B.enq(y) A.enq(x) A.enq(y) A.deq(y) B.deq(x) There may be more than one order that satisfies these conditions Both are in program order 7 Consistency Quiescent and sequential consistency are incomparable: The one does not necessarily exist when the other exists Sequential consistency Sequential consistency is not compositional Quiescent consistency does not necessarily preserve program order Sequential consistency is unaffected by quiescent periods Sequential consistency To overcome the drawback of sequential consistency not being compositional, we replace the requirement that method calls be in program order Principle 3.5.1 Each method call should appear to take effect instantaneously at some moment between its invocation and response Linearizability Principle 3.5.1 defines a third correctness property: Linearizability Linearizability Each method should “take effect” Instantaneously Between invocation and response events Each linearizable execution is sequentially consistent, but not vice versa Object is correct if this “sequential” behavior is correct Any such concurrent object is Linearizable™ 8 Linearizability To show that a concurrent object is linearizable one should identify for each method a linearization point where the method takes effect Linearization points For lock-based implementations: Critical section For other methods: The single step where the effects of the method call become visible to other methods Single-enqueuer/single-dequeuer No critical section Linearization points depend on execution If deq() returns a value: Linearization point = head field is updated Linearizability Sequential consistency is good way to describe standalone systems Linearizability is good way to describe components of large system If list is empty: Linearization point = deq() throws an exception Example Example q.enq(x) time time 9 Example Example q.enq(x) q.enq(y) time q.enq(x) q.enq(y) q.deq(x) time Example Example lin ea riz a q.enq(x) q.enq(y) q.deq(x) time q.deq(y) q.enq(x) q.enq(y) q.deq(x) time q.deq(y) bl e Example Example q.enq(x) q.enq(y) q.deq(x) time q.deq(y) Va lid ? time (5) Art of Multiprocessor Programming 60 10 Example Example q.enq(x) q.enq(x) q.deq(y) time (5) time 61 (5) Art of Multiprocessor Programming Art of Multiprocessor Programming 62 Example Example q.enq(x) q.deq(y) q.enq(y) time q.enq(x) q.deq(y) q.enq(y) time (5) Art of Multiprocessor Programming 63 (5) Art of Multiprocessor Programming 64 Example no t q.deq(y) q.enq(y) time Example lin ea riz ab q.enq(x) le time 65 (4) (5) Art of Multiprocessor Programming Art of Multiprocessor Programming 66 11 Example Example q.enq(x) q.enq(x) q.deq(x) time (4) time 67 (4) Art of Multiprocessor Programming Art of Multiprocessor Programming 68 Example Example lin ea riz a q.enq(x) q.deq(x) time (4) q.enq(x) q.deq(x) time 69 (4) bl e Art of Multiprocessor Programming Art of Multiprocessor Programming 70 Correctness Three correctness conditions: Quiescent consistency Applications that require high performance with weak constraints on object behaviour Correctness Safety property Deals with correctness of concurrent execution In correct order? No collisions? Sequential consistency Describe low-level systems such as hardware memory interfaces Linearizability Describe higher-level systems composed of linearizable components 12 Quiescent consistency Checks that method calls appear to be made in sequential order If write 7 and then -3 a read should not be -7 Sequential consistency Checks that method calls appear to be made in sequential order If write 7 and then -3 a read should not be -7 AND Checks that method calls are in real-time order We do not care about the order of concurrent method calls, but when separated by a period of inactivity, method calls should take place in the correct order AND Checks that method calls are made in program order If write 7 and then -3 a read should not be 7 Linearizability Checks that method calls appear to take place instantaneously Linearization points If one method’s linearization point is in the correct program order than a overlapping method, those methods are linearizable Progress Liveness property Deals with if different threads have to wait For how long? Will they ever reach the critical section? Progress Progress guarantees: Blocking Delay of any one thread can delay others Lock-free A method is lock-free if some method calls finishes in a finite number of steps Do not acquire a lock to enter the critical section Allow multiple threads to attempt to make progress independently of one another Non-blocking Delay of one thread cannot delay the others 13 Wait-free A method is wait-free if it guarantees that every call finishes its execution in a finite number of steps It is bounded wait-free is there is a limit on the number of steps a method call can take Lock-free vs. wait-free A non-blocking algorithm is: Lock-free if there is guaranteed system-wide progress Wait-free if there is also per-thread progress Progress Conditions Deadlock-free: some thread trying to acquire the lock eventually succeeds. Starvation-free: every thread trying to acquire the lock eventually succeeds. Lock-free: some thread calling a method eventually returns (succeeds) Wait-free: every thread calling a method eventually returns (succeeds) Progress Conditions Non-Blocking Everyone makes progress Someone makes progress Blocking Wait-free Lock-free Starvation-free Deadlock-free Java Memory Model Java programming language does not guarantee linearizability when reading and writing fields of shared objects Due to compiler optimization memory reads and writes are often reordered Singleton object getInstance() public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } 14 Singleton object Create a single instance of the class Method must guard against multiple threads each seeing instance to be null and create new instances Once the instance has been created, however no further synchronization should be necessary Singleton object getInstance() public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } Problem Singleton object getInstance() public static Singleton getInstance() { synchronized(this) synchronized(this) { if (instance == null) instance = new Singleton(); } Lock down return instance; critical section to } Singleton object getInstance() public static Singleton getInstance() { if (instance == null) { synchronized(this) { synchronized(this) if (instance == null) instance = new Singleton(); } Double-checked locking } return instance; } avoid collisions But what about optimization? In theory correct, this is however incorrect! Singleton object In theory, the constructor call takes place before the instance field is assigned However, the java memory model allows these steps to occur out of order = making a partially initialized Singleton object visible to other programs Java memory model In the Java memory model: Objects reside in shared memory Each thread has a private working memory that contains cached copies of fields it has read or written 15 Java memory model In the absence of explicit synchronization: A thread that writes to a field may not update the memory right away, and A thread that reads from a field may not update its working memory if the field’s value in memory changes Synchronization events Synchronization usually implies mutual exclusion In Java, is also implies reconciling a thread’s working memory with the shared memory Synchronization events In Java usually in one of two ways: Cause a thread to write changes back to shared memory immediately Cause thread to invalidate its working memory values and forces it to reread the fields from shared memory Synchronization events Synchronization events are linearizable: They are ordered All threads agree on the ordering Synchronization events Locks and synchronized blocks Volatile fields Final fields Locks and synchronized blocks Thread can achieve mutual exclusion through implicit lock (synchronized block) or explicit lock If all accesses to a particular field are protected by the same lock, then the reads-writes to that field is linearizable 16 Locks and synchronized blocks When a thread releases a lock the changes are written to shared memory immediately When a thread acquires a lock it invalidates its own memory and rereads the value from shared memory Volatile fields Reading a volatile field is like acquiring a lock – value is reread from shared memory W riting a volatile field is like releasing a lock – changes immediately written to shared memory Volatile fields However, multiple reads-writes are not successful Some form of mutual exclusion is then needed Final fields A field declared to be final cannot be modified once it has been initialized in its constructor Final fields class FinalFieldExample { final int x; int y; FinalFieldExample; static FinalFieldExample; FinalFieldExample() public FinalFieldExample() { x = 3; y = 4; } static void reader() { if (f != null) f.x; f.y; int i = f.x; int j = f.y; } } Final fields public class EventListener { final int x; es) public EventListener(EventSource es) { es.registerListener(this); es.registerListener(this); x = 7; } public onEvent(Event e) { Registers EventSource object … Correct! Thread that calls reader() is guaranteed to see x equal to 3 If another thread calls onEvent before the constructor finishes, then onEvent is not guaranteed correct value for x 17 In summary Reads-writes to fields are linearizable if: The field is volatile The field is protected by a unique lock used by all readers and writers 18 ...
View Full Document

Ask a homework question - tutors are online