Testing the lock-free algorithms
- Define the requirements of the algorithm in a formal way
- Devise theoretical algorithm (pseudo code)
- Proof of correctness (by hand or automated theorem prover, e.g. TLA+)
- Implement algorithm
- Code review by experts
- Unit tests to check single-threaded correctness (necessary condition)
- Multi-threaded stress tests (check invariants, no data structure corruption)
- Code instrumentation for specific multi-threaded scenarios (open problem …)
- Verification tools such as CppMem to check the data races etc. (for small code snippets)
Our New Friend Compare Exchange
- Allows us to implement useful algorithms to exchange data in a lock-free way
- These techniques are especially useful in real-time systems
- Care must be taken when manipulating data concurrency without locks
Key observations
- CAS loops are the basis for many lock-free algorithms
- CAS can only be used on small data (64 bit usually)
- CAS can be used to transfer memory ownership atomically between threads
- Lock-free algorithms usually require managing memory and object lifetime carefully
- We can add information (e.g. counters) to atomically check for cuncurrent modification with CAS
Disadvantages
- CAS is rather expensive… but so is locking with contention
- Algorithms for simple problems get rather complex
- Lock-free algorithms are hard to test
Implementation
Notes
- Replacing std::optional - allocates dynamic memory, most likely not lock-free .Replacing with variation that allocates on stack
- The copy/move cosntructor of T needs to be lock-free - Case of trivially copyable types
- memcpy is lock-free but not atomic