Flow Modifiers
Flow modifiers control how statements execute and when they yield control.
Executes all statements indivisibly, without yielding:
atomic action UpdateBoth:
x = x + 1
y = y + 1
# Both updates happen atomically
Use cases:
- Database transactions
- Critical sections
- Operations that must complete together
Yields between statements, allowing interleaving:
action UpdateBoth: # serial is default
x = x + 1 # Yield point after this
y = y + 1 # Yield point after this
Use cases:
- Multi-step operations
- Modeling real-world sequencing
- Exploring crash scenarios
Yield points are where the model checker can schedule other actions or inject faults.
Yield points occur:
- After each simple Python statement (in serial context)
- At the end of atomic blocks
- Before and after fizz function calls
- Examples: assignments,
pass, method calls
NO yield points after:
- Control flow keywords:
if,elif,else - Loop keywords:
for,while - Flow control:
break,continue,return - Guards:
require - Non-determinism:
any,oneof(yield after the full statement) - Inside atomic blocks
Examples:
action SerialExample:
x = 1 # Yield point after
y = 2 # Yield point after
if x > 0: # NO yield point
z = 3 # Yield point after
atomic action AtomicExample:
x = 1 # NO yield point (inside atomic)
y = 2 # NO yield point
# Yield point here (end of atomic block)
action MixedExample:
x = 1 # Yield point
atomic:
y = 2 # NO yield (inside atomic)
z = 3 # NO yield
# Yield point here (end of atomic)
w = 4 # Yield point
Important for:
- Crash injection (crashes happen at yield points)
- Action interleaving (other actions can run)
- Understanding execution model
Executes statements concurrently:
atomic action UpdateConcurrently:
parallel:
x = x + 1
y = y + 1
# Explores all interleavings of the two updates
Use cases:
- Concurrent operations
- Parallel threads
- Race condition detection
Nondeterministically chooses one branch:
atomic action MakeChoice:
oneof:
x = 1
x = 2
x = 3
# Explores all three possibilities
Use cases:
- Modeling failures (success or failure)
- Non-deterministic choices
- Alternative execution paths
Flow modifiers can be nested:
action Example:
serial:
step1 = True # Yield after this
atomic:
# These execute together
step2 = True
step3 = True
Atomic blocks execute all statements as one indivisible step No yields or context switches occur within the block
Demonstrates: atomic flow modifier on blocks
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
FAILED: Model checker failed. Invariant: Consistent
------
Init
--
state: {"a":0,"b":0}
------
IncrementSerial
--
state: {"a":1,"b":0}
------
Serial blocks yield after each statement (default behavior) Allows interleaving with other actions between statements
Demonstrates: serial flow modifier (explicit form of default)
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
Nodes: 10, queued: 0
Valid Nodes: 10 Unique states: 9
IsLive: true
PASSED: Model checker completed successfully
Parallel blocks execute statements concurrently Explores all possible interleavings of the statements
Demonstrates: parallel flow modifier for concurrent execution
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
Nodes: 69, queued: 0
Valid Nodes: 69 Unique states: 56
IsLive: true
PASSED: Model checker completed successfully
Oneof blocks create nondeterministic choice Exactly ONE of the statements is executed per transition
Demonstrates: oneof flow modifier for branching state space
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
Nodes: 5, queued: 0
Valid Nodes: 5 Unique states: 4
IsLive: true
PASSED: Model checker completed successfully
Counterexample traces through parallel blocks render correctly
Demonstrates: threads parked at a parallel join have no program counter; the failure renderer must handle them
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
FAILED: Model checker failed. Invariant: NoOverflow
------
Init
--
state: {"a":0,"b":0}
------
Both
--
state: {"a":0,"b":0}
------
Stmt:0
--
state: {"a":1,"b":0}
------
Both
--
state: {"a":1,"b":0}
------
Stmt:0
--
state: {"a":2,"b":0}
------
Flow modifiers can be nested with different behaviors
Demonstrates: nested atomic within serial, serial within atomic
|
|
Expected result (from fizz):
StateSpaceOptions: options:{max_actions:100 max_concurrent_actions:2}
Nodes: 5206, queued: 0
Skipping dotfile generation. Too many Nodes: 5206
Valid Nodes: 5206 Unique states: 5200
IsLive: true
PASSED: Model checker completed successfully
- Mutex Pattern
- Read-Write Lock Pattern
- Distributed Lock Pattern
- Optimistic Locking Pattern
- Saga Pattern
- Snapshot Isolation Pattern
- Compare-And-Swap Pattern
- Producer-Consumer Pattern
- Message Queue Pattern
- Cache Coherence Pattern
- Eventual Consistency Pattern
- Idempotent Operations Pattern
- Circuit Breaker Pattern
- Rate Limiting Pattern
- Write-Ahead Log Pattern
- Event Sourcing Pattern