See README.md for project overview and usage.
No issues were found. The lock-free __contains__ implementation passed all stress tests.
The patch lock_free_change.patch modifies CPython's Objects/setobject.c to make
__contains__ lock-free. Key code paths to understand:
set_lookkey_threadsafe()- Lock-free lookup path for free-threaded buildsset_compare_threadsafe()- Entry comparison with_Py_TryIncrefCompareensure_shared_on_read()- Marks set as shared on first cross-thread accessset_table_resize()- Resize with QSBR cleanup of old tableset_swap_bodies()- Swaps two sets' internals (used by__iand__, etc.)
PySet_MINSIZE = 8- Threshold between smalltable and malloc'd tableSET_LOOKKEY_CHANGED = -2- Signals that set was mutated during lookup
The implementation handles these concurrent scenarios:
- Table pointer changes during lookup - Resize sets
table=NULLtemporarily - Entry key changes during comparison - Detected via
startkey != ep->keycheck - QSBR delayed freeing - Old tables freed after grace period when set is shared
- Mask/table ordering -
maskupdated beforetableduring resize
| File | Purpose |
|---|---|
main.py |
Main stress test with 6 modes |
test_mutating_keys.py |
Tests keys that mutate set in __hash__/__eq__ |
test_resize_race.py |
Targeted resize race condition tests |
run_tests.sh |
Runs all tests |
| Mode | Target | Verification |
|---|---|---|
standard |
Basic concurrent read/write | Contiguous value ranges |
resize |
set_table_resize() + QSBR |
Crash/exception only |
swap |
set_swap_bodies() via __iand__ |
Crash/exception only |
discard |
Dummy entry handling | Crash/exception only |
hash_collision |
Linear probing paths | Crash/exception only |
threshold |
Small/large table transitions | Crash/exception only |
- Modes with
clear()cannot verify contiguous ranges - Multiple writers callingclear()wipe each other's work. These modes only check for crashes/exceptions. - Only
standardmode verifies value integrity - Writers have non-overlapping ranges, noclear()calls. - Use
threading.Barrier- Ensures all threads start simultaneously for maximum contention.
# Quick test
./run_tests.sh --quick
# Full test suite
./run_tests.sh
# Thorough (longer duration, more threads)
./run_tests.sh --thorough
# Single mode
$PYTHON main.py --mode resize --duration 10 --readers 8
# Mutating keys (exercises SET_LOOKKEY_CHANGED path)
$PYTHON test_mutating_keys.py --duration 5- If a test fails with "Missing N values", check if that mode uses
clear()- may be a test bug, not implementation bug. test_resize_race.py --test Nruns individual targeted tests to isolate issues.- Increase
--durationand--readersto increase likelihood of hitting race windows. - The
hash_collisionmode forces linear probing by using keys with identical hashes.
These are ideas for further stress testing if needed:
frozensetoperations (immutable, different code paths)- Set operations that iterate (
union,intersection,difference) - Weak references to set elements during concurrent modification
- TSAN/ASAN builds for memory safety verification