Coarse-Grained Synchronization Ogni metodo opera mediante un lock sull oggetto Code di attesa per operare sull oggetto Nozione di correttezza (linearizability) Modello astratto basato sulla nozione di storia Tecniche statiche (+ model checking) E fatta? 1 Coarse-Grained Synchronization Operare con thread Non aumenta necessariamente l efficienza complessiva di un sistema Attese attive, overhead di gestione, runtime complicato, Multiprocessori? Alcune app sono inerentemente parallele map&reduce come usato da Google 2 1
Fine-Grained Synchronization Non utilizziamo un lock globale Strutturiamo l oggetto in un insieme di lock Independently-synchronized components Metodi sono in conflitto quando cercano di accedere Medesima componente contemoraneamente Optimistic Synchronization Si cerca la componente richiesta senza usare lock Una volta trovata OK: e fatta Oops: riprova Valutazione Semplice Errori di programmazione sono costosi 2
Metodi add(x) remove(x) contains(x) Interface: Set List-Based Sets public interface Set<T> { public boolean add(t x); public boolean remove(t x); public boolean contains(t x); 3
public class Node { public T item; public int key; public Node next; List Node public class Node { public T item; public int key; public Node next; List Node item of interest 4
public class Node { public T item; public int key; public Node next; List Node Usually hash code public class Node { public T item; public int key; public Node next; List Node Reference to next node 5
List-Based Set - a b c + Ordinata con sentinelle (chiavi min & max) Preservato da add() Invariante remove() contains() Solita induzione sulla struttura 6
Rep: Funzione di astrazione a b Astrattamente: {a, b Rep Invariant Rep invariant Definisce quali sono le rappresentazioni legali valide Preservato dai metodi Dipende dai metodi 7
Sentinelle Ordinamento Nessun duplicato Rep Invariant S(head) = Abstraction Map { x esiste a tale che a raggoungibile da head e a.item = x 8
List Based Set Add(b) a c d Remove(b) a b c List Based Set Add(b) a c d Remove(b) b a b c 9
Coarse-Grained Locking a b d Coarse-Grained Locking a b d c 10
Coarse-Grained Locking a b d c Semplice e costoso Coarse-Grained Locking Metodi sincronizzati (a la Java) Solo un metodo ha l accesso Corretto Thread multipli Code di attesa Overhead 22 11
Fine-grained Locking Pensare prima di programmare Suddividere l oggetto in parti Ogni parte dell oggetto ha un suo lock Metodi che operano su parti disgiunte possono operare senza problema di sincronizzazione 23 a b c 12
a b c a b c 13
a b c a b c 14
Remove a b c d remove(b) Remove a b c d remove(b) 15
Remove a b c d remove(b) Remove a b c d remove(b) 32 16
Remove a b c d remove(b) Remove a c d remove(b) Problema 2 lock? 17
Concurrent Remove a b c d remove(b) remove(c) Concurrent Remove a b c d remove(b) remove(c) 18
Concurrent Remove a b c d remove(b) remove(c) Concurrent Remove a b c d remove(b) remove(c) 19
Concurrent Remove a b c d remove(b) remove(c) Concurrent Remove a b c d remove(b) remove(c) 20
Concurrent Remove a b c d remove(b) remove(c) Concurrent Remove a b c d remove(b) remove(c) 21
Concurrent Remove a b c d remove(b) remove(c) Art of Multiprocessor Programming 43 Concurrent Remove a b c d remove(b) remove(c) 44 22
a c d remove(b) remove(c) c non viene rimosso a c d remove(b) remove(c) 23
Quale e il problema? Rimuovere c Operare sul campo next di b a b c a b c Cerchiamo di capire il problema Se un nodo e lock Nessuno puo cancellare il nodo successore Morale: il thread deve fare il lock Sul nodo da cancellare E sul predecessore 24
a b c d remove(b) a b c d remove(b) 25
a b c d remove(b) a b c d remove(b) trovato 26
a b c d remove(b) trovat o a c d remove(b) 27
a b c d remove(b) remove(c) a b c d remove(b) remove(c) 28
a b c d remove(b) remove(c) a b c d remove(b) remove(c) 29
a b c d remove(b) remove(c) a b c d remove(b) remove(c) 30
a b c d remove(b) remove(c) a b c d remove(b) remove(c) 31
a b c d wait remove(c) a b c d remove(c) wait 32
a b c d Wait! remove(c) a b d Notify 33
a b d remove(b) a b d remove(b) 34
a d remove(b) a d 35
Remove method public boolean remove(item item) { int key = item.hashcode(); Node pred, curr; try { finally { curr.unlock(); pred.unlock(); Remove method public boolean remove(item item) { int key = item.hashcode(); Node pred, curr; try { finally { curr.unlock(); pred.unlock(); Key used to order node 36
Remove method public boolean remove(item item) { int key = item.hashcode(); Node pred, curr; try { finally { currnode.unlock(); prednode.unlock(); Predecessor and current nodes Remove method public boolean remove(item item) { int key = item.hashcode(); Node pred, curr; try { finally { curr.unlock(); pred.unlock(); Make sure locks released 37
Remove method public boolean remove(item item) { int key = item.hashcode(); Node pred, curr; try { finally { curr.unlock(); pred.unlock(); Everything else Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); finally { 38
Remove method lock pred == head try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); finally { Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); finally { Lock current 39
Remove method try { pred = this.head; pred.lock(); curr = pred.next; curr.lock(); finally { Traversing list Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; 40
Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; Search key range pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; At start of each loop: curr and pred locked 41
Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); If return item false; found, remove node Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return If node false; found, remove it 42
Remove: searching Unlock predecessor while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Remove: searching Only one node locked! while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; 43
Remove: searching while (curr.key <= key) { demote current if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Remove: searching while (curr.key <= key) { if Find (item and == lock curr.item) new current { pred.next = curr.next; return true; pred.unlock(); pred = currnode; curr = curr.next; curr.lock(); return false; 44
Remove: searching while (curr.key <= key) { Lock if (item invariant == curr.item) restored { pred.next = curr.next; return true; pred.unlock(); pred = currnode; curr = curr.next; curr.lock(); return false; Remove: searching while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; Otherwise, not present pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; 45
Why does this work? To remove node e Must lock e Must lock e s predecessor Therefore, if you lock a node It can t be removed And neither can its successor remove() e linearizable while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; pred raggiungibile da head curr e pred.next curr.item appartiene 46
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Linearization point if item is present Why remove() is linearizable while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Node locked, so no other thread can remove it. 47
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; Item not present curr.lock(); return false; while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; pred.unlock(); pred = curr; curr = curr.next; pred reachable from head curr.lock(); curr is pred.next pred.key < key return false; key < curr.key 48
while (curr.key <= key) { if (item == curr.item) { pred.next = curr.next; return true; Linearization point pred.unlock(); pred = curr; curr = curr.next; curr.lock(); return false; Add e lock predecessor lock successor Inserzione 49
Tutto bene? Sicuramente meglio del lock globale Thread possono scorrere in parallelo Comportamento ideale? Catena di lock/unlock Potrebbe non essere efficiente Optimistic: scorrere senza fare Lock a b d e add(c) Aha! 50
Optimistic a b d e add(c) Optimistic c a b d e add(c) 102 51
Cosa potrebbe andare storto? a b d e add(c) Aha! Cosa potrebbe andare storto? a b d e add(c) 104 52
Cosa potrebbe andare storto? a b d e remove(b) 105 Cosa potrebbe andare storto? a b d e remove(b) 106 53
Cosa potrebbe andare storto? a b d e add(c) 107 Cosa potrebbe andare storto? c a b d e add(c) 108 54
Cosa potrebbe andare storto? a d e add(c) 109 a b d e add(c) b raggiungibile da head 55
Ancora problemi a b d e add(c) Aha! 111 Ancora problemi a b d e add(c) add(b ) 56
Ancora problemi a b d e add(c) b add(b ) 113 Ancora problemi a b d e add(c) b 114 57
Ancora problemi c a b d e add(c) 115 a b d e add(c) Verifica, b punta a d 58
Optimistic: Linearization Point a b d e add(c) c Validation private boolean validate(node pred, Node curry) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; 59
Validation private boolean validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; Predecessor & return false; current nodes private boolean validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; Validation Begin at the beginning 60
Validation private boolean validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; Search range of keys Validation private boolean validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; Predecessor reachable 61
Validation private boolean validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; Is current node next? Validation private boolean Otherwise move on validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; 62
Validation private boolean Predecessor not reachable validate(node pred, Node curr) { Node node = head; while (node.key <= pred.key) { if (node == pred) return pred.next == curr; node = node.next; return false; Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; 63
Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; Search key Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; Retry on synchronization conflict 64
Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; Examine predecessor and current nodes Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; Search by key 65
Remove: searching public boolean remove(item item) { int key = item.hashcode(); retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; Stop if we find item Remove: searching public boolean remove(item item) { int key Move = item.hashcode(); along retry: while (true) { Node pred = this.head; Node curr = pred.next; while (curr.key <= key) { if (item == curr.item) break; pred = curr; curr = curr.next; 66
Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; else { return false; finally { pred.unlock(); curr.unlock(); Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; else { return false; Always unlock finally { pred.unlock(); curr.unlock(); 67
Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; else { return false; finally { Lock both nodes pred.unlock(); curr.unlock(); Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; else { Check for synchronization return false; conflicts finally { pred.unlock(); curr.unlock(); 68
Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; else { return false; finally { pred.unlock(); curr.unlock(); target found, remove node Remove Method try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.item == item) { pred.next = curr.next; return true; target not found else { return false; finally { pred.unlock(); curr.unlock(); 69
Valutazione Buona gestione dei lock Performance Concurrency Problemi Scorrere la lista due volte contains() comunque richiede lock remove() Scan list Lazy List Lock predecessor & current Logical delete Marcare il nodo come eliminato (!!!!) Physical delete Ridirezionare i puntatori 70
Lazy Removal a b c d Lazy Removal a b c d Present in list 71
Lazy Removal a b c d Logically deleted 143 Lazy Removal a b c d Physically deleted 72
Lazy Removal a b d Physically deleted a b c 73
a b c a b c 74
a b c remove(b) a b c a not marked 75
a b c a still points to b a b c Logical delete 76
a b c physical delete a b c 77
S(head) = Abstraction Map { x esiste a tale che a raggiungibile da head e a.item = x e aunmarked Invariant If not marked then item in the set and reachable from head and if not yet traversed it is reachable from pred 78
Validation private boolean validate(node pred, Node curr) { return!pred.marked &&!curr.marked && pred.next == curr); List Validate Method private boolean validate(node pred, Node curr) { return!pred.marked &&!curr.marked && pred.next == curr); Predecessor not Logically removed 79
List Validate Method private boolean validate(node pred, Node curr) { return!pred.marked &&!curr.marked && pred.next == curr); Current not Logically removed List Validate Method private boolean validate(node pred, Node curr) { return!pred.marked &&!curr.marked && pred.next == curr); Predecessor still Points to current 80
Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; else { return false; finally { pred.unlock(); curr.unlock(); Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; else { Validate as before return false; finally { pred.unlock(); curr.unlock(); 81
Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; else { return false; finally { Key found pred.unlock(); curr.unlock(); Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; else { return false; finally { pred.unlock(); Logical remove curr.unlock(); 82
Remove try { pred.lock(); curr.lock(); if (validate(pred,curr) { if (curr.key == key) { curr.marked = true; pred.next = curr.next; return true; else { return false; finally { pred.unlock(); physical remove curr.unlock(); Contains public boolean contains(item item) { int key = item.hashcode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; return curr.key == key &&!curr.marked; 83
Contains public boolean contains(item item) { int key = item.hashcode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; return curr.key == key &&!curr.marked; Start at the head Contains public boolean contains(item item) { int key = item.hashcode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; return curr.key == key &&!curr.marked; Search key range 84
Contains public boolean contains(item item) { int key = item.hashcode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; return curr.key == key &&!curr.marked; Traverse without locking (nodes may have been removed) Contains public boolean contains(item item) { int key = item.hashcode(); Node curr = this.head; while (curr.key < key) { curr = curr.next; return curr.key == key &&!curr.marked; Present and undeleted? 85
Lock-free Lists Logical Removal a 0 b 0 c 10 e 0 Physical Removal Non basta 171 Problema Logical Removal a 0 b 0 c 10 e 0 Physical Removal d 0 Node added 86
Bit di marcatura e puntatori a 0 b 0 c 10 e 0 AtomicMarkableReference Logical Removal = Set Mark Bit d 0 AtomicMarkableReference Operazione atomica Modificare il puntatore Modificare la marcatura Remove (due passi) Operare sul mark bit del campo next Modificare il predecessor 87
In Java AtomicMarkableReference class Java.util.concurrent.atomic package Reference address F mark bit Extracting Reference & Mark Public Object get(boolean[] marked); 88
Extracting Reference & Mark Public Object get(boolean[] marked); Returns reference Returns mark at array index 0! Extracting Reference Only public boolean ismarked(); Value of mark 89
Changing State Public boolean compareandset( Object expectedref, Object updateref, boolean expectedmark, boolean updatemark); Changing State If this is the current reference Public boolean compareandset( Object expectedref, Object updateref, boolean expectedmark, boolean updatemark); And this is the current mark 90
Changing State then change to this new reference Public boolean compareandset( Object expectedref, Object updateref, boolean expectedmark, boolean updatemark); and this new mark Changing State public boolean attemptmark( Object expectedref, boolean updatemark); 91
Changing State public boolean attemptmark( Object expectedref, boolean updatemark); If this is the current reference Changing State public boolean attemptmark( Object expectedref, boolean updatemark);.. then change to this new mark. 92
To Lock or Not to Lock Locking vs. Non-blocking: visioni estreme La risposta: compinare blocking e non blocking Esempi: Lazy list :blocking add() eremove() con wait-free contains() 93