# This test captures the concurrency bug discovered in issue #7524. # # InvariantService stored invariants (_invariants dict) and enforcement records # (_enforcement_records list) without any threading.Lock. Concurrent calls from # parallel plan execution could raise RuntimeError: dictionary changed size during # iteration or silently corrupt data. # # The fix adds a threading.RLock to InvariantService guarding all shared mutable # state (see commit that implements the fix). @tdd_issue @tdd_issue_7524 Feature: TDD Issue #7524 - InvariantService Thread Safety InvariantService must be thread-safe so that concurrent calls from parallel plan execution threads cannot race on _invariants or _enforcement_records. Scenario Outline: Concurrent add_invariant() must not corrupt the dict Given an invariant service for thread-safety testing When threads concurrently add invariants through a barrier Then the service has no RuntimeError raised during concurrent access And the service should have exactly active invariants And all added invariants should be retrievable via get_invariant_snapshot Examples: | n | count | total | | 2 | 5 | 10 | | 4 | 5 | 20 | | 8 | 3 | 24 | | 10| 2 | 20 | Scenario Outline: Concurrent list_invariants() must not raise during iteration Given an invariant service with pre-existing invariants When 5 threads concurrently list all invariants through a barrier Then no thread raised RuntimeError during concurrent reads And the caller should receive a valid list of at least active invariants Examples: | count | min_count | | 10 | 10 | | 20 | 20 | | 5 | 5 | Scenario Outline: Concurrent remove_invariant() must not race with add Given an invariant service for thread-safety testing When threads concurrently add invariants while concurrent threads remove different ones through a barrier Then no thread raised RuntimeError during mixed access And the service should have a consistent count of active invariants Examples: | n | m | | 5 | 2 | | 4 | 3 | | 8 | 4 | Scenario Outline: Concurrent enforce_invariants() must not corrupt the record list Given an invariant service with invariants to enforce When threads concurrently call enforce_invariants on the same set through a barrier Then no thread raised RuntimeError during enforcement And each thread should have received exactly enforcement records Examples: | count | n | total | | 3 | 4 | 3 | | 5 | 8 | 5 | | 2 | 10 | 2 | Scenario Outline: Concurrent enforce and list must both succeed Given an invariant service with invariants already enforced by a previous thread When threads concurrently enforce new invariants while other threads list them through a barrier Then no thread raised RuntimeError during mixed enforcement and listing And the enforcement record count should be consistent across all threads Examples: | count | n | | 3 | 4 | | 5 | 6 |