Temporallogik und parallele Programme 260 Softwaresysteme allgemein (1) Bisher: • Sequentielle Programme • Relationale Semantik: Anfangs- und Endzustand • Spezifikation: Vor- und Nachbedingung • „Programm ist Algorithmus der ein Ergebnis berechnet“ Zweck: Spezifikation von Systemoperationen Grundannahme: Die Operationen sind atomar, i. e. nicht unterbrechbar, zeitliche Dauer ist vernachlässigbar (Meist) adäquat bei sequentiellen Programmen und einzelne Systemoperationen. 261 Softwaresysteme allgemein (2) Die Sicht als atomare Operation ist nicht adäquat für • Abläufe von Softwaresystemen, die eine Reihe von Systemoperationen aufrufen (vgl. Szenarios in Use Cases/ Abläufe von abstr. Datentypen) • eingebettete Systeme, die eine Umgebung haben (Benutzer, die Eingaben machen; Sensoren/Aktoren) • Programme, die nicht terminieren sollen (Betriebssysteme, Protokolle, Web-Server etc.) • Einzelne Threads, die parallel zu anderen laufen (gegenseitige Beeinflussung) Für all diese Systeme müssen Abläufe von Programmen betrachtet werden, die in einer Umgebung stattfinden. Ziel der heutigen Vorlesung: Definition einer dazu passenden Logik. 262 Motivation - Beispielprogram semaphore ≡ while true do await S > 0; S := S − 1; semaphore while true do await S > 0; S := S − 1; (: critical section :) (: critical section :) L1 : skip; S := S + 1; L2 : skip; S := S + 1; (: noncritical section :) (: noncritical section :) skip; skip; Eigenschaften: • Sicherheit (engl. safety): Zu keinem Zeitpunkt sind beide Prozesse gleichzeitig im kritischen Bereich • Lebendigkeit/Fortschritt (engl. liveness/progress): Ein Prozess betritt irgendwann seinen kritischen Bereich 263 Parallele Programme: Interleaving Die Wahl der Programmiersprache trifft 2 zentrale Entscheidungen: Entscheidung 1: Programme werden interleaved parallel ausgeführt f • Ausführung von x:= x + 1 x := x ∗ 2; x:= x ∗ 2 mit x = 0 gestartet dauert 3 Schritte und ergibt ⋆ x = 4 (linker Schritt zuerst) oder ⋆ x = 2 (erster Schritt rechts, dann links, dann rechts), oder ⋆ x = 1 (beide Schritte rechts zuerst). • Adäquat für Threads/Prozesse auf einem Rechner mit globalem Speicher • Multi-Core Prozessoren: Hardware sorgt immer dafür, dass ein Speicherzugriff „gewinnt“ (Stichwort: cache coherence Protocols) • Allerdings: Wir betrachten nur sog. sequential consistency: i.e. es gibt eine interleavete Reihenfolge für Speicherzugriffe • Prozessoren haben wegen Caching schwächere Speicher-Modelle • Java Memory Model ist noch schwächer 264 (da es Code-Umordnung durch den Compiler erlauben muss) Alternativen zu Interleaving Alternative 1 zu Entscheidung 1: Asynchron parallel • x :=1 ||a y := 2 dauert 2 oder einen Schritt • Typischer Formalismus: Statecharts • Echte Gleichzeitigkeit möglich • Problem: Was tun bei „Clashes“: x := 5 ||a x := 6? (abort, exception, einer gewinnt zufällig, sequentiell, ausschliessen etc.) Alternative 2 zu Entscheidung 1: Synchron parallel • x :=1 ||s y := 2 dauert genau einen Schritt • „Getaktet“ • Typischer Formalismen: Alle Hardware-Modellierungssprachen • Clashes meist syntaktisch verboten („meta-stabile“ Zustände) 265 Parallele Programme: Kommunikation Entscheidung 2: Kommunikation zwischen parallel ausgeführten Programmen geschieht über gemeinsame Variablen (oder gemeinsamen Speicher; engl. shared variables). • Adäquat für gemeinsamen Speicher/gemeinsame Resourcen Alternative zu Entscheidung 2: Kommunikation durch Synchronisation • Typische Sprachen: CSP, CCS, IO-Automaten, Sprachen mit send und receive-Kommandos • Konzepte: Signale, Kanäle, Events • Adäquat für Kommunikation zwischen Rechnern 266 Traces (Intervalle) Ziel: Kalkül, der die Untersuchung verteilter Systeme ermöglicht 1. Abläufe definieren (Traces) 2. Formeln reden über alle Zwischenzustände eines Programms (irgendwann, immer, solange . . . bis etc.) 3. Semantik für Programme, die alle Zwischenzustände berücksichtigt Grundlegende Definition Ein Zustandsfolge (z0 , . . . , zn ) Trace ist eine mit n ∈ IN ∪ {∞} • Die Semantik von Programmen ist eine Menge möglicher Traces • Formeln sind wahr oder falsch auf einem Trace • Zustand zi im Trace = Variablenbelegung (wie bisher) • Wenn endliche Traces erlaubt sind, heißen sie auch Intervalle 267 Temporallogik vs. Relationale Semantik TL-Semantik von Programmen α: Einer oder mehrere Traces. Die relationale Semantik ist „gröber“ und daraus herleitbar: Deterministische Programme: • Nur ein möglicher Trace (z0 , . . . , zn ) für einen Startzustand z0 • Wenn endlich (z0 , zn ) ∈ [[α]] • Wenn unendlich, dann kein Paar (z0 , z) in [[α]] Nichtdeterministische Programme: • Evtl. mehrere mögliche Traces (z0 , . . . , zn ) für einen Startzustand z0 • Jeder endliche liefert (z0 , zn ) ∈ [[α]] • Falls mindestens ein unendlicher: z0 6∈ α ↓ 268 Intervalle mit Umgebungsschritten Wir betrachten Programme mit einer Umgebung • Z. B. die physikalische Umgebung des Programms • Bei zwei interleaveten Programmen läuft das eine Programm in der Umgebung des anderen ⇒ „lokale Analyse eines Threads“ ⇒ (engl. thread-local reasoning) • Beachte: Die bisherigen sequentiellen Programme werden jetzt innerhalb einer Umgebung betrachtet • In einem Ablauf (z0 , z′0 , z1 , z′1 . . . , zn ) wechseln sich ab ⋆ Systemschritte (z0 , z′0 ), (z1 , z′1 ), . . . des Programms ⋆ Umgebungsschritte (z′0 , z1 ) , (z′1 , z2 ), . . . • Der erste Schritt eines Traces ist ein Systemschritt • Falls endlich, ist der letzte Schritt ein Umgebungsschritt 269 Semantik temporallogischer Formeln • Temporallogische Formeln sind über einem Ablauf wahr oder falsch: A, (z0 , z′0 , z1 , . . . , zn ) |= ϕ • A ist die zugrundeliegenden Algebra (Datenstruktur), die Modell der Spezifikation ist (im folgenden weggelassen). • Normale prädikatenlogische Aussagen ϕ sind temporallogische Formeln. Sie beziehen sich nur auf den Anfangszustand: (z0 , z′0 , z1 , . . . , zn ) |= ϕ ⇔ z0 |= ϕ (dasselbe gilt auch für Dynamische Logik) • Über zukünftige Zustände redet man mit temporallogischen Operatoren oder mit Variablen. 270 Variablen in Temporallogik • Statische Variablen haben immer denselben Wert im Ablauf. (Konvention in KIV: Mit Kleinbuchstaben) • Flexible Variablen ändern ihren Wert: X, X′ , X′′ bezeichnen die Werte in z0 , z′0 , z1 (Konvention in KIV: Großbuchstaben) • Konvention: Falls der Ablauf nur aus einem Zustand besteht, haben X′ und X′′ auch den Wert von X in z0 . • Definition variables X : nat flexible; • Die Formeln X = X′ und X′ = X′′ besagen: Der erste System- bzw. Umgebungsschritt ändert X nicht • Zuweisung X := T macht einen Systemschritt und impliziert X′ = T 271 Temporallogische Formeln (1) Informelle Beschreibung: 2 ϕ d.h. ϕ gilt jetzt und in jedem künftigen Zustand (always). 3 ϕ d.h. ϕ gilt jetzt oder in einem künftigen Zustand (eventually ). Formale Beschreibung (es gilt: 3 ϕ ↔ ¬ 2 ¬ ϕ): (z0 , z′0 , z1 . . . , zn ) |= 2 ϕ ⇔ Für alle 0 ≤ i ≤ n gilt: (zi , . . . , zn ) |= ϕ (z0 , z′0 , z1 , . . . , zn ) |= 3 ϕ ⇔ Es gibt ein 0 ≤ i ≤ n mit (zi , . . . , zn ) |= ϕ Beispiel: 2 X′ = X′′ ≡ „Die Umgebung modifiziert X nie.“ Beispiel: 2 (X = 5 ∧ X′ = 5) ≡ „X ist 5 vor und nach jedem Systemschritt“ Beispiel: 2 (X = 5 → 3 X > 5) ≡ „Wenn X irgendwann den Wert 5 hat, wird es irgendwann später erhöht“ 272 Temporallogische Formeln (2) Informelle Beschreibung: ϕ until ψ d.h. ψ gilt irgendwann, und davor gilt stets ϕ. ϕ unless ψ d.h. ϕ gilt immer, bis ψ gilt. Formale Beschreibung: (z0 , . . . , zn ) |= ϕ until ψ ⇔ Es gibt ein 0 ≤ i ≤ n mit: (zi , . . . , zn ) |= ψ ∧ ∀ 0 ≤ j < i.(zj , . . . , zn ) |= ϕ (z0 , . . . , zn ) |= ϕ unless ψ ⇔ (2 ϕ) ∨ (ϕ until ψ) Beispiel: N = N′′ + 1 until N = 0 ≡ „N wird bis auf 0 heruntergezählt“ Beispiel: Y′′ = Y′ unless X′ 6= X ≡ „Die Umgebung ändert Y solange nicht, wie das System X nicht modifiziert“ 273 Temporallogische Formeln (3) Informelle Beschreibung: ◦ ϕ d.h. es gibt einen Folgezustand in dem ϕ gilt (strong next). • ϕ d.h. wenn ein Folgezustand existiert, gilt darin ϕ (weak next). last d.h. der aktuelle Zustand ist der letzte. Formale Beschreibung: (z0 , . . . , zn ) |= ◦ ϕ ⇔ n 6= 0 ∧ (z1 , . . . , zn ) |= ϕ (z0 , . . . , zn ) |= • ϕ ⇔ n = 0 ∨ (z1 , . . . , zn ) |= ϕ (z0 , . . . , zn ) |= last ⇔ n = 0 Beispiel: Eine einzelne Zuweisung impliziert ◦ last ≡ „genau ein Schritt“ Beispiel: 3 last ≡ „endlicher Ablauf = Terminierung“ Beispiel: 2 ϕ ↔ ϕ ∧ • 2 ϕ „ϕ gilt immer gdw. ϕ gilt jetzt, und falls nächster Zustand ex., dann gilt ϕ ab diesem immer“ 274 Programme sind Formeln • Programme α beschreiben eine Menge möglicher Traces • Definiere (z0 , . . . , zn ) |= α als: Der Trace (z0 , . . . , zn ) ist ein möglicher Ablauf des Programms α. • Somit ist ein Programm „wahr“ auf einem Trace, wenn der Trace ein möglicher Ablauf von α ist. • Ergebnis: Programme sind auch einfach spezielle Formeln! • Programme beschreiben nur Systemschritte in einer beliebigen Umgebung 275 Zuweisung als Formel • Temporallogische Zuweisungen nur für flexible Variablen • X := T impliziert X′ = T und ◦ last • Problem: Was ist mit den anderen Variablen? ⇒ Programmvariablen sollten unverändert bleiben • Deshalb: Programmvariablen (relevant für alle Zuweisungen im Programm) werden explizit durch Variablenliste vl angegeben: [: vl | α] • Definition: ρvl ≡ V {X′ = X | X ∈ vl} • Damit: [: vl | X := T] ↔ X′ = T ∧ ρvl\X ∧ ◦ last Beispiel: [: X, Y, Z | X := 1] ↔ X′ = 1 ∧ Y′ = Y ∧ Z′ = Z ∧ ◦ last 276 Typische TL Beweisziele α, I, E ⊢ P • α ist ein Programm • I ist eine Startbedingung (Prädikatenlogik) • E ist eine Annahme an die Umgebung • P ist eine zu zeigende Eigenschaft „Programm α gestartet im Zustand I mit Umgebung E hat Eigenschaft P“ 277 Beispiel: Beweisverpflichtung semaphore (: P arallelesP rogramm :) [: S, L1 , L2 | semaphore] , (: U mgebungsannahme :) 2 (S ′′ = S ′ ∧ L′′1 = L′1 ∧ L′′2 = L′2 ) (: Initialzustand :) S = 1, ¬ L1 , ¬ L2 ⊢ (: Beweisverpf lichtung :) 2 ¬ (L1 ∧ L2 ) „Programm semaphore erlaubt es nie, dass beide Teilprogramme gleichzeitig im kritischen Abschnitt sind.“ 278 Semantik Paralleler Programme Zuweisung: vl | X := t • X′ = t • Alle anderen Variablen in vl unverändert: ρvl \ X • Transition nicht blockiert; terminiert nach einem Schritt No operation: vl | skip • Alle Variablen in vl unverändert: ρvl • Transition nicht blockiert; terminiert nach einem Schritt 279 Semantik Paralleler Programme Sequentielle Komposition: vl | α; β • Erst Transitionen von vl | α • Wenn α terminiert, dann Transitionen von vl | β Konditional: vl | if ε then α else β • Auswertung von ε benötigt einen Schritt • Falls ε gilt, dann Transitionen von vl | α, sonst von vl | β Schleife: vl | while ε do α • Auswertung von ε benötigt einen Schritt • Solange ε gilt, Transitionen von vl | α, sonst Terminierung 280 Semantik Paralleler Programme Synchronisation: vl | await ε • Auswertung von ε dauert keinen Schritt • Sobald ε gilt, Terminierung • Bis dahin: ⋆ Alle Variablen in vl unverändert: ρvl ⋆ Transition ist blockiert Interleaving: vl | α f β • Entweder nächste (nicht blockierte) Transition von vl | α oder von vl | β • Falls beide Programme blockiert, dann Interleaving blockiert (und ρvl ) 281 Kalkül: Symbolische Ausführung • In Dyn. Logik: Berechne aus Vorbedingung Formel, die nach einem (Programm)schritt gilt. • In TL: Gehe einen Schritt im Ablauf vorwärts (geht auch für Formeln) • Spezialfall, wenn es keinen weiteren Schritt gibt (i. e. last gilt) Dann haben X, X′ , X′′ alle denselben Wert x0 (neue stat. Variable) TL-Formeln zu PL reduzierbar, z. B.: last → (2 ϕ ↔ ϕ) • Im Normalfall gibt es einen Schritt. • Idee: Ersetze X,X′ ,X′′ durch x0 , x1 , X (mit neuen stat. Variablen x0 , x1 ) um einen Schritt vorwärts zu gehen • Aus ◦ ϕ wird einfach ϕ • Beispiel: 2 (X = 0 ∧ X′ = 1) ↔ X = 0 ∧ X′ = 1 ∧ • 2 (X = 0 ∧ X′ = 1) • Ergebnis der Ausführung deshalb x0 = 0 ∧ x1 = 1 ∧ 2 (X = 0 ∧ X′ = 1) 282 Kalkül: Die Step-Regel Symbolische Ausführung mittels der Step - Regel: L(Γ) ⊢ L(∆) S(Γ) ⊢ S(∆) step Γ⊢∆ • Funktion L(.) liefert eine Formel, falls kein weiterer Schritt folgt. • Funktion S(.) liefert eine Formel, die einem Schritt ausgeführt hat. Ausführung von prädikatenlogischen Formeln ϕ (X′ , X′′ sind erlaubt): Speziell: 0 ,x0 ,x0 L(ϕ) :≡ ϕxX,X ′ ,X ′′ 0 ,x1 ,X S(ϕ) :≡ ϕxX,X ′ ,X ′′ 283 Kalkül: Symbolische Ausführung Ausführung von TL Formeln: ϕ L(ϕ) S(ϕ) 2ϕ 3ϕ ϕ until ψ ϕ unless ψ •ϕ ◦ϕ L(ϕ) L(ϕ) L(ψ) L(ϕ) ∨ L(ψ) true false true S(ϕ) ∧ 2 ϕ S(ϕ) ∨ 3 ϕ S(ψ) ∨ S(ϕ) ∧ ϕ until ψ S(ψ) ∨ S(ϕ) ∧ ϕ unless ψ ϕ ϕ false last 284 Kalkül: Symbolische Ausführung Ausführung paralleler Programme: L(.) α L(α) vl | X := t vl | skip vl | α; β vl | if ε then α else β vl | while ε do α vl | await ε f vl | α β false false L(vl | α) ∧ L(vl | β) false false L(ε) L(vl | α) ∧ L(vl | β) 285 Kalkül: Symbolische Ausführung Ausführung paralleler Programme: Stepfunktion S baut auf T auf: T : VLPROG → P(FMA × {F, T} × (VLPROG ∪ {E})) • T beschreibt die Menge der ersten Transitionen • FMA: Beziehung zwischen ungestrichenen und gestrichenen Variablen. • {F, T}: Flag das angibt, ob die Transition blockiert ist (T). • VLPROG ∪ {E}: Restprogramm, falls vorhanden, sonst: E. 0 ,x1 ,X • τvl := (ρvl )xX,X ′ ,X ′′ = { x0 = x1 | X ∈ vl} 286 Kalkül: Symbolische Ausführung α T (α) vl | X := t vl | skip {(x1 = S(t) ∧ τvl \ X , F, E)} {(τvl , F, E)} {(L(vl | α) ∧ ϕ, b, vl | β ′ ) | (ϕ, b, vl | β ′ ) ∈ T (vl | β) ∪ {(ϕ, b, vl | α′ ; β) | (ϕ, b, vl | α′ ) ∈ T (vl | α)} {(S(ε) ∧ τvl , F, vl | α), (S(¬ ε) ∧ τvl , F, vl | β)} vl | α; β vl | if ε then α else β vl | while ε do α {(S(ε) ∧ τvl , F, vl | α; while ε do α), (S(¬ ε) ∧ τvl , F, E)} 287 Kalkül: Symbolische Ausführung α T (α) vl | await ε f vl | α β {(¬ S(ε) ∧ τvl , T, vl | await ε)} f ′ {(ϕ, F, vl | α β) | (ϕ, F, vl | α′ ) ∈ T (vl | α)} f ′ ∪ {(ϕ, F, vl | α β ) | (ϕ, F, vl | β ′ ) ∈ T (vl | β)} f ′ ′ ∪ {(ϕ ∧ ψ, T, vl | α β ) | (ϕ, T, vl | α′ ) ∈ T (vl | α), (ψ, T, vl | β ′ ) ∈ T (vl | β)} Die Stepfunktion S ist dann definiert als: S([: vl | α]) :≡ W {ϕ ∧ ¬ blk0 ∧ [: vl | α′ ] | (ϕ, F, vl | α′ ) ∈ T (vl | α)} W ∨ {ϕ ∧ blk0 ∧ [: vl | α′ ] | (ϕ, T, vl | α′ ) ∈ T (vl | α)} 288 Kalkül: VD Induktion (1) Wie bisher vorhanden: Noethersche Induktion. Jetzt zusätzlich VD Induction und apply VD Induction. • VD = Verification Diagram • Erlaubt Induktion für Beweisziele: ⊢ 2 ϕ • Beweisidee durch Widerspruch: Wenn 2 ϕ falsch ist, gibt es einen ersten Zustand, in dem ϕ nicht gilt. • ¬ (2 ϕ) ↔ ∃ N. N = N′′ + 1 until ¬ ϕ • N wird solange heruntergezählt, bis erstmals ¬ ϕ gilt. • Jetzt geht Induktion über den Wert von N = die Zahl der Schritte • Regel VD Induction codiert dies in einen Schritt 289 Kalkül: VD Induktion (2) • VD induction „verzögert“ die Erstellung einer Induktionshypothese: Es entsteht nur ein anonymes Prädikat Indhyp(N). • apply VD Induction erlaubt als Induktionshypothese jedes Beweisziel, das mit weniger als N Schritten erreicht wurde. • Dazu muss im Beweisbaum auf den Knoten, der als Induktionshypothese verwendet werden soll, geklickt werden. • Idee zur Verwendung: Sobald sich in einem Programm ein vorheriger Zustand wiederholt, Induktionshypothese anwenden • Der Zustand darf aus einem anderen Beweisast sein, er muss aber weniger steps ausgeführt haben (sonst ist er nicht noethersch kleiner) 290 Kalkül: Induktion apply VD induction 291 Kalkül: Sequentialisierung • Häufig ergibt das Ausführen von interleaveten Programmen trotz unterschiedlicher Reihenfolge zweimal dasselbe Goal f f • Beispiel: x := 5; α y := 6; β ergibt 2 goals mit α β • Regel insert proof lemma wendet einen Beweisknoten als Lemma in einem anderen Beweisast an. • Anwendung durch Klicken auf das anzuwendende Lemma • Spart die Definition eines separaten Lemmas • Nur anwendbar bei Azyklizität! Regel insert proof lemma <proved in a different proof branch> V W Γ0 ⊢ ∆0 Γ, Γ0 → ∆0 ⊢ ∆ Γ⊢∆ 292 Kalkül: Sequentialisierung Insert Proof Lemma 293 Demo (: Programm :) [: X | while true do { X := 1 } f { await X = 1; X := 0; }], (: Umgebung :) 2 X′′ = X′ , (: Initialisierung :) X=0 (: Eigenschaft :) ⊢ 2 X ≤ 1; 294