170 likes | 302 Views
Distributed Algorithms - Ricart Agrawala Mutual Exclusion Datum: 26. Juni 2000 Vortragende: Thomas Lehner Rene Mayrhofer. Problemstellung. gegeben: - asynchrones Netzwerk - eine Ressource - mehrere konkurrierende Prozesse und deren User gewünscht:
E N D
Distributed Algorithms - Ricart Agrawala Mutual Exclusion Datum: 26. Juni 2000 Vortragende: Thomas Lehner Rene Mayrhofer
Problemstellung gegeben: - asynchrones Netzwerk - eine Ressource - mehrere konkurrierende Prozesse und deren User gewünscht: - immer nur ein Prozess greift zugleich auf Ressource zu Beispiel: Drucker Problemlösung Grundlegende Idee des Ricart Agrawala Algorithmus: - Einführung einer Critical Region - Prozesse dürfen nur aus dieser auf Ressource zugreifen - Prozesse sprechen sich untereinander ab, so dass sich immer nur einer zugleich in der Critical Region befindet exklusiver Zugriff auf Ressource
Problemlösung User-Prozess Interaktion: Einzuhaltende Bedingungen: Well-formedness: - die User-Prozess Interaktion ist wohlgeformt Mutual exclution: - es ist kein globaler Zustand erreichbar, in dem sich mehr als ein Prozess in C befindet Progress: - wenn kein Prozess in C und zumindest einer in T ist, dann muss irgendwann einer in C eintreten - wenn zumindest ein Prozess in E ist, erreicht irgendwann einer auch R Lockout-freedom: - wenn alle User C immer verlassen, dann kommt ein Prozess der in T ist auch irgendwann in C - jeder der in E ist kommt auch irgendwann in R
Der Ricart AgrawalaME Algorithmus - Jeder Prozess besitzt eine logische Uhr. Diese wird erhöht bei jedem Empfangs-/Sendeereignis und bei jeder User-Prozess Interaktion. - Es existieren die beiden Nachrichten TRY und OK. - Ein Prozess i kann nur dann in C eintreten, wenn er ein TRY-Broadcast durchgeführt hat und von alle anderen Prozessen die dazugehörigen OK’s erhielt. - Ein Prozess i darf nur dann mit einem OK auf ein (“try”,c) von j antworten wenn: 1. Pi ist in E, R oder in T und das tryi wurde noch nicht versand. 2. Pi ist in T und (c,j)<(c´,i) mit (c´,“try“) ist letztes TRY von Pi - In allen anderen Fällen wird das OK für Pj zwischengespeichert und erst in E an Pj versand.
Beweis für Safety Beweis für Mutual Exclution durch indirekte Beweisführung: Annahme: Pi und Pj sind zu gleich in C mit (c(last tryi),i)<(c(last tryj),j) es existieren die dazugehörigen zwei TRY und OK Nachrichten, die von Pj an Pi bzw. von Pi an Pj geschickt wurden, bevor die Prozesse in C eintraten zwei Mögliche Zeitpunkte an denen das TRY von Pj bei Pi ankam: - i hat sein TRY noch nicht versendet: dann kann Pi kein TRY mit (c,i)<(c(last tryj),j) mehr broadcasten - i hat sein TRY bereits abgeschickt: dann kann Pj kein OK von Pi erhalten, da ja nach Annahme gilt (c(last tryi),i)<(c(last tryj),j) und somit Pi das OK zurückhält bis Region Ei erreicht wird
Beweis für Liveness ·Beweis für Progress: (für Region E trivial) - Der Beweis für die Region T durch indirekten Beweis: Annahme: Zum Zeitpunkt a ist zumindest ein Prozess in T und keiner erreicht je C --> es existiert ein Zeitpunkt b vor a an dem alle Prozesse in R oder in T bleiben --> es existiert ein Zeitpunkt c nach b an dem alle Prozesse in T ihre TRY versand haben und keine dieser Nachrichten mehr in den Nachrichtenkanälen ist --> für einen Pi in T gilt nun: (c(latest Tryi),i)<(c(latest Tryj),j) für alle Pj in T Da Pi nicht in C eintritt, muss es mindestens einen Pj geben dessen OK fehlt Die 2 Gründe, weshalb Pj das OK zurückhalten kann: - Pj ist in C. Widerspruch, da Pj in R oder in T ist - Pj ist in T und erhielt ein Tryj mit (c(Tryj),j)<(c(latest Tryk),k) für alle Pk in T (k j) Widerspruch, da gilt: (c(latest Tryi),i)<(c(latest Tryj),j) ·Beweis für Lockout-freedom: (für Region E trivial) - Der Beweis für die Region T durch indirekten Beweis: Annahme: Pi ist in T mit versendetem TRY und tritt nie in C ein nach dem maximal n*(c(latest tryi)-1) + i-1 mal ein Prozess in C eintrat, tritt kein weiterer Pj (ji)mehr in C ein, da dann gelten würde (c(latest tryj),j)>(c(latest tryi),i) Widerspruch zu Beweis von Progress
Formale Spezifikation - 1 Node i ( i {1, ....., N} ): Signature: Input: tryi exiti recieve(m)j, i, m {"try", "ok"} N, 1 <= j <= N Output: criti remi send(m)i, j, m {"ok"} N, j i bcast(m)i, m {"try"} N State Variables: regioni {R, T, C, E}, initially R clocki N, initially 0 bcast-bufferi, a FIFO queue of {"try"}, initially empty for every j, 1 <= j, <= N: history(j)i, a subset of {"try", "ok"} N, initially {} for every j ¹ i: send-buffer(j)i, a FIFO queue of {"ok"}, initially empty deferred-ok(j)i, a FIFO queue of {"ok"}, initially empty Transitions: tryi Effect: clocki := clocki + 1 regioni := T add ("try") to bcast-bufferi recieve(m, c)j, i Effect: clocki := max(clocki, c) + 1 history(j)i := history(j)i {(m, c)} if m = "try" and j i then if regioni {E, R} or (regioni = T and bcast-bufferi contains ("try")) then add ("ok") to send-buffer(j)i else if regioni = C then add ("ok") to deferred-ok(j)i else if regioni = T and not bcast-bufferi contains ("try") if (c', i) > (c, j) with ("try", c') history(i)i and (c'' with ("try", c'') history(i)i and c'' > c') then add ("ok") to send-buffer(j)i else add ("ok") to deferred-ok(j)i
Formale Spezifikation - 2 exiti Effect: clocki := clocki + 1 regioni := E for all j i for all (m, c) in deferred-ok(j)i add (m, c) to send-buffer(j)i clear deferred-ok(j)i criti Precondition: regioni = T for such a c that ("try", c) history(i)i and (c'' with ("try", c'') history(i)i and c'' > c) for all j i ("ok", c') history(j)i with (c, i) < (c', j) Effect: clocki := clock + 1 regioni := C remi Precondition: regioni = E Effect: clocki := clocki +1 regioni = R send(m, c)i, j Precondition: m ist first on send-buffer(j)i c = clocki + 1 Effect: clocki := clocki +1 remove first element of send-buffer(j)i bcast(m, c)i Precondition: m ist first on bcast-bufferi c = clocki + 1 Effect: clocki := clocki +1 remove first element of bcast-bufferi Tasks: { criti } { remi } { bcast(m)i } for every j i: { send(m)i, j }
package MutualExclusion; import daj.*; import java.util.*; public class RicartAgrawala extends Application { public static final int PNUM = 6; public static final int DISTANCE = 70; public static final int BORDER = 40; .... public void construct() { Node[] nodes = new Node[PNUM]; int i, j; double phi, r = (PNUM * DISTANCE) / (2 * Math.PI); for (i=0; i<PNUM; i++) { phi = (double) i / PNUM * Math.PI * 2; nodes[i] = node(new Prog(i), new Integer(i).toString(), (int) (r * Math.cos(phi) + r + BORDER), (int) (r * Math.sin(phi) + r + BORDER)); } for (i=0; i<PNUM; i++) { for (j=0; j<PNUM; j++) { link(nodes[i], nodes[j]); } } } .... }
class Prog extends Program { ... /** the node number */ protected int index; /** the region can be either R, T, C or E */ protected int region = R; /** the current clock value */ protected int clock = 0; /** for every node, the message history */ protected Vector[] history = null; /** for every neighbour, the buffer for remembering deferred ok messages until critical region has been left */ protected Vector[] deferred_ok = null; /** the logical time of the last "try" message that has been sent */ protected LogicalTime lastTryTime = null; /** the default constructor initializes the index and the arrays */ public Prog(int index) { this.index = index; history = new Vector[RicartAgrawala.PNUM]; for (int i=0; i<history.length; i++) history[i] = new Vector(); deferred_ok = new Vector[RicartAgrawala.PNUM]; for (int i=0; i<deferred_ok.length; i++) if (i != index) deferred_ok[i] = new Vector(); else deferred_ok[i] = null; }
public void tryRessource() { // single actions must be atomic --> synchronize them synchronized (this) { clock++; region = T; lastTryTime = new LogicalTime(clock, index); out().send(new Msg(Msg.TRY, lastTryTime)); } } public void freeRessource() { synchronized (this) { clock++; // this does not match the formal specification - the release is // done immediately region = R; for (int i=0; i<deferred_ok.length; i++) if (i != index) { for (int j=0; j<deferred_ok[i].size(); j++) { out(i).send((Msg) deferred_ok[i].get(j)); } deferred_ok[i].removeAllElements(); } } } public void ressourceAvailable() { // for testing purposes there is no user to ne notified } public void ressourceFreed() { // for testing purposes there is no user to ne notified }
public void main() { Random rand = new Random(); GlobalAssertion safety = new CriticalRegionConflict(), liveness = new CriticalRegionLockout(); while (true) { assert(safety); assert(liveness); /* first the tree construction routines */ int received = in().select(2); if (received >= 0) { synchronized (this) { Msg msg = (Msg) in(received).receive(); if (msg.time.clock > clock) clock = msg.time.clock; clock++; history[received].add(msg); // respond to try requests from other nodes if (msg.type == Msg.TRY && received != index) handleTryMessage(received, msg); // check if the critical region can be entered now if (region == T && checkOkMsgsReceived()) { clock++; region = C; // the last try is invalid now lastTryTime = null; } } } // simulate the user here ... } }
private void handleTryMessage(int received, Msg msg) { Msg ackMsg = new Msg(Msg.OK, new LogicalTime(clock, index)); if (region == R /*|| region == E*/) out(received).send(ackMsg); else if (region == C) deferred_ok[received].add(ackMsg); else if (region == T) { if (lastTryTime != null && msg.time.lessThan(lastTryTime)) out(received).send(ackMsg); else deferred_ok[received].add(ackMsg); } } private boolean checkOkMsgsReceived() { boolean receivedOkMsgs = true; for (int i=0; i<history.length; i++) { if (i != index) { int j = history[i].size() - 1; while (j >= 0 && ((Msg) history[i].get(j)).type != Msg.OK) j--; if (j >= 0) { Msg lastMsg = (Msg) history[i].get(j); if (! lastTryTime.lessThan(lastMsg.time)) receivedOkMsgs = false; } else receivedOkMsgs = false; } } return receivedOkMsgs; } }
class LogicalTime { /** the clock value */ public int clock; /** for equal clock values the sender process's index is compared */ public int process; public LogicalTime(int clock, int process) { this.clock = clock; this.process = process; } public boolean equals(Object o) { if (o instanceof LogicalTime && ((LogicalTime) o).clock == clock && ((LogicalTime) o).process == process) return true; else return false; } public boolean lessThan(LogicalTime t) { if (clock < t.clock || (clock == t.clock && process < t.process)) return true; else return false; } }
class CriticalRegionConflict extends GlobalAssertion { private Vector procsInCR = new Vector(); public boolean assert(Program progs[]) { int numOfProcsInCR = 0; procsInCR.removeAllElements(); for (int i=0; i<progs.length; i++) { if (((Prog) progs[i]).region == Prog.C) { numOfProcsInCR++; procsInCR.add(new Integer(i)); } } return numOfProcsInCR <= 1; } public String getText() { String text = "Processes in the critical region: "; for (int i=0; i<procsInCR.size(); i++) text += procsInCR.get(i) + " "; return text; } }
class CriticalRegionLockout extends GlobalAssertion { private LogicalTime[] tryTimes = new LogicalTime[RicartAgrawala.PNUM]; private int procInCR = -1, procTryingLonger = -1; public CriticalRegionLockout() { for (int i=0; i<tryTimes.length; i++) tryTimes[i] = null; } public boolean assert(Program progs[]) { for (int i=0; i<progs.length; i++) if (((Prog) progs[i]).region == Prog.T) tryTimes[i] = ((Prog) progs[i]).lastTryTime; // Now check when a process is in the CR, if another one is still // trying but started to try earlier. This should not happen. for (int i=0; i<progs.length; i++) if (((Prog) progs[i]).region == Prog.C) { for (int j=0; j<progs.length; j++) { if (((Prog) progs[j]).region == Prog.T && tryTimes[j].lessThan(tryTimes[i])) { procInCR = i; procTryingLonger = j; return false; } } } return true; } }