Minimalist Thread Sanitizer in Rust
This challenge asks you to implement a simplified version of a thread sanitizer (TSan) in Rust. TSan is a powerful tool that detects data races and other memory safety issues in multithreaded programs. While a full TSan implementation is incredibly complex, this challenge focuses on a core aspect: detecting simple data races on shared mutable variables.
Problem Description
You are to create a library that wraps a shared mutable variable and provides a mechanism to detect data races when accessed from multiple threads. The library should track accesses to the variable and report a data race if two or more threads attempt to write to the variable concurrently, or if one thread attempts to write while another is reading. The core of the solution is a SharedMutable struct that encapsulates the data and the race detection logic.
Key Requirements:
- SharedMutable Struct: This struct should hold the data to be shared and internal state for tracking accesses.
- Access Tracking: Implement methods
read()andwrite()onSharedMutable. These methods should record the thread ID of the accessing thread and the type of access (read or write). - Data Race Detection: After each
write()operation, check if a data race has occurred. A data race occurs if:- Another thread is currently writing to the variable.
- A thread is currently reading the variable.
- Reporting: If a data race is detected, the
write()method should panic with a descriptive error message including the thread IDs involved and the type of access. - Thread Safety: The
SharedMutablestruct itself must be thread-safe.
Expected Behavior:
- Multiple threads can safely read the shared variable concurrently.
- Multiple threads can safely write to the shared variable sequentially (one after the other).
- Concurrent writes from multiple threads should trigger a panic.
- A write while another thread is reading should trigger a panic.
- Reads should not trigger panics.
Edge Cases to Consider:
- What happens if a thread attempts to read or write to the variable multiple times?
- How to handle the case where a thread attempts to write to the variable while it is already in the process of writing? (This should still be considered a data race).
- The internal tracking mechanism should be thread-safe.
Examples
Example 1:
Input: Two threads, both attempting to write to a `SharedMutable<i32>` concurrently.
Output: Panic with a message indicating a data race.
Explanation: The `write()` method detects that another thread is currently writing, triggering the panic.
Example 2:
Input: Thread 1 reads from a `SharedMutable<String>`, then Thread 2 writes to it.
Output: No panic.
Explanation: Reading does not trigger a data race.
Example 3:
Input: Thread 1 writes to a `SharedMutable<bool>`, then Thread 2 reads from it, then Thread 3 writes to it.
Output: Panic with a message indicating a data race.
Explanation: Thread 3's write occurs while Thread 2 is reading, triggering the panic.
Constraints
- The shared data type can be any type that implements the
Copytrait. - Thread IDs can be represented as
u64. - The library should be as efficient as possible while maintaining correctness. Avoid unnecessary locking if possible.
- The panic message should be clear and informative, including the thread IDs and the type of access that caused the race.
- The solution must be thread-safe.
Notes
- You can use Rust's standard library for threading and synchronization primitives (e.g.,
Mutex,RwLock,Arc). However, the core race detection logic should be implemented manually. - Consider using an
enumto represent the access type (Read, Write). - Think carefully about how to track the current access and the thread ID of the accessing thread. A simple
Mutexmight be a good starting point, but explore alternatives for potential performance improvements. - This is a simplified TSan implementation. It does not cover all possible data race scenarios (e.g., aliasing, memory ordering). The focus is on detecting simple concurrent writes and writes while reading.
- Testing is crucial. Write thorough unit tests to cover various scenarios, including concurrent reads, concurrent writes, and writes while reading.