Table of Contents

Liveness Checking Algorithm in TLC

The algorithm is described in the book: "temporal verification of reactive systems: safety"

High-Level Overview

The liveness checking algorithm in TLA+ verifies temporal properties in specifications - properties that describe how a system should behave over time. Unlike safety properties (which check that bad things never happen), liveness properties assert that something good eventually happens.

The algorithm works by:

  1. Converting temporal formulas to a specific normal form
  2. Building a behavior graph that combines the state space with a tableau
  3. Searching for strongly connected components (cycles) in this graph
  4. Checking if any cycle violates the liveness properties

If a violation is found, the algorithm constructs a counterexample to show the problematic behavior. LiveWorker.java:33-41

Formula Conversion and Preprocessing

Step 1: Processing Liveness Properties

The algorithm starts by parsing the liveness properties defined in the specification and converting them to a workable form:

  1. Temporal formulas are parsed and converted to LiveExprNode objects through the astToLive method
  2. The formulas are tagged, simplified, and converted to Disjunctive Normal Form (DNF)
  3. The DNF is then classified into different types of expressions:
    • AEAction: Eventually Always Actions ( []<>A )
    • EAAction: Always Eventually Actions ( <>[]A )
    • AEState: Eventually Always States ( []<>S )
    • tfs: Temporal formulas with no actions

Liveness.java:498-533

Step 2: Creation of Tableau and Order of Solution

For each temporal formula, the algorithm creates:

  1. A tableau graph representation
  2. An "Order of Solution" that contains the tableaux and checks to perform

The Order of Solution organizes how the algorithm will verify the different components of liveness properties.

Liveness.java:682-777

Behavior Graph Construction

Step 3: Building the Behavior Graph

The algorithm constructs a behavior graph that combines:

  1. The state space graph from model checking
  2. The tableau representation of the liveness properties

This combined graph is stored using a disk-based representation (DiskGraph or TableauDiskGraph).

LiveCheck.java:522-615

When the behavior graph has tableaux, a more complex graph construction is used: LiveCheck.java:617-709

Strongly Connected Component Detection

Step 4: Checking for Cycles Using Tarjan's Algorithm

The core of the liveness checking is performed by the LiveWorker class, which searches for strongly connected components (SCCs) in the behavior graph:

  1. The algorithm uses an iterative version of Tarjan's algorithm to find SCCs
  2. It focuses on finding cycles that might violate liveness properties
  3. For each SCC found, it immediately checks if it violates the liveness properties

LiveWorker.java:117-147

The main SCC search algorithm: LiveWorker.java:229-319

Step 5: Checking Components for Violations

For each SCC found, the algorithm checks if it violates the liveness properties by examining:

  1. AEState conditions: Eventually Always State predicates
  2. AEAction conditions: Eventually Always Action predicates
  3. Promises: Special temporal conditions that must be fulfilled

LiveWorker.java:530-546

The core violation check: LiveWorker.java:736-766

Counterexample Generation

Step 6: Building Error Traces

If a violation is found, the algorithm constructs a counterexample by:

  1. Creating a prefix path from an initial state to the SCC
  2. Identifying the cycle within the SCC that demonstrates the violation
  3. Combining these into a full trace that shows the liveness violation

LiveWorker.java:822-853 LiveWorker.java:887-896

Optimization and Scheduling

The liveness checking is not performed after every state exploration step, as that would be too expensive. Instead, the algorithm:

  1. Runs liveness checking periodically based on how much the behavior graph has grown
  2. Uses a threshold to determine when to run the check
  3. Always performs a final check on the complete graph

LiveCheck.java:171-200 LiveCheck.java:225-242

Parallel Execution

The algorithm can leverage multiple cores by:

  1. Creating multiple LiveWorker threads to check different parts of the behavior graph
  2. Using a queue to distribute the work among the workers
  3. Synchronizing the results and error reporting

LiveCheck.java:262-276 LiveCheck.java:289-300

Notes