Checking Liveness
Liveness for the resource consumption example!
Let's start from the following solution to the resource consumption example:
------------------------------- MODULE cache -------------------------------
EXTENDS Integers, Sequences, TLC
CONSTANT ResourceCap, MaxConsumerReq
ASSUME ResourceCap > 0
ASSUME MaxConsumerReq \in 1..ResourceCap
(*--algorithm cache
variables
reserved = 0,
resources_left = ResourceCap;
define
ResourceInvariant == resources_left >= 0
end define;
process actor \in {"actor1", "actor2"}
variables
resources_needed \in 1..MaxConsumerReq
begin
WaitForResources:
while TRUE do
when resources_left - reserved >= resources_needed;
reserved := reserved + resources_needed;
UseResources:
while resources_needed > 0 do
resources_left := resources_left - 1;
resources_needed := resources_needed - 1;
reserved := reserved - 1
end while;
with x \in 1..MaxConsumerReq do
resources_needed := x;
end with;
end while;
end process;
process time = "time"
begin
Tick:
resources_left := ResourceCap;
goto Tick;
end process;
end algorithm; *)
=============================================================================
Now let's consider liveness. Let's state that the first process can eventually reach "UseResources". You can use this definition: MayAlwaysUse == <>(pc["actor1"]="UseResources").
- Create a model with ResourceCap=6 and MaxConsumerReq=2. What is the minimal fairness assumptions needed for TLA to accept this?
- Change the MaxConsumerReq to 4. Does this change anything? Why?
Peterson's algorithm
Create a new project peterson.tla. Start with the code for Dekker's algorithm that we discussed during the lecture: threads.tla. Modify this to implement Peterson's algorithm. You will find it useful to have a thread-local variable other = CHOOSE t \in Threads : t /= self.
- Run and make sure the sanity checks (AtMostOneCritical, Deadlocks, NoLiveLocks) pass.
- Make sure you have kept the outer loop (P5: goto P1 in the original code). Termination checking should fail, but the other liveness condition should succeed.
- Anticipate what happens if you change the goto into
P5: goto P2. - Now, for the most important exercise, change the liveness condition so that
P5: goto P4fails, butP5: goto P1succeeds.
------------------------------ MODULE peterson ------------------------------ EXTENDS TLC, Integers CONSTANT Threads (*--algorithm peterson variables flag = [t \in Threads |-> FALSE], turn \in Threads; define OnlyOneCritical == \A t1, t2 \in Threads: pc[t1] = "CS" /\ pc[t2] = "CS" => t1 = t2 NoLiveLocks == \A t \in Threads: <>(pc[t] = "CS") end define fair process thread \in Threads variable other = CHOOSE t \in Threads : t /= self begin P1: skip; P2: skip; P3: skip; CS: skip; P4: skip; P5: goto P1 end process; end algorithm; *) =============================================================================
Ferryman
A classic problem in model checking is the ferryman problem... You have a ferryman, goat, cabbage, and wolf who want to cross the river. The ferryman can cross with at most one passenger. The following combination of items violate the invariant of things remaining uneaten:
- the goat and the cabbage on the same side without ferryman
- the goat and the wolf on the same side without ferryman
Can the ferryman transport get them all over to the other side? Or more precisely, can TLA find a way to transport them over?
Here is the spec, but you should add the temporal logic formula for a solution trace to exist. We will then ask TLA to disprove that there is a solution! (It will then give us a trace of the solution to prove us wrong...)
------------------------------ MODULE ferryman ------------------------------
goods == {"goat", "cabbage", "wolf", "ferryman"}
(* --algorithm ferryman
variables
position = [item \in goods |-> "left"],
current_item = "init" (*just for nicer trace!*)
define
flip(dir) == CASE dir = "left" -> "right" [] dir = "right" -> "left"
currentside == position["ferryman"]
otherside == flip(currentside)
currentside_items == {it \in goods : position[it] = currentside}
Safe == \/ position["goat"] = position["wolf"]
\/ position["goat"] = position["cabbage"]
=> position["ferryman"] = position["goat"]
Solved == \A item \in goods : position[item] = "right"
Solution == TRUE (* replace with your formula *)
ThereIsNoSolution == ~Solution
end define
begin Ferry:
while TRUE do
with item \in currentside_items do (* picking ferryman to go alone *)
position[item] := otherside || position["ferryman"] := otherside;
current_item := item
end with
end while
end algorithm*)
=============================================================================