Skip to content

dotnet-7/thread-sentry

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Thread-Sentry

High-Performance Deadlock and Race Condition Detection Engine
Production-ready concurrent safety monitoring with < 5% overhead

License Rust Performance

δΈ­ζ–‡ζ–‡ζ‘£ | Usage Guide | Performance Report


🎯 Why Thread-Sentry?

The Problem

Writing concurrent programs in Rust/C++/Go is challenging. The two most dreaded issues:

  • Deadlocks: Program freezes after running for days
  • Data Races: Data corruption without any visible symptoms

Current Solutions

ThreadSanitizer (TSan):

  • βœ… Detects issues
  • ❌ 10-50x performance overhead
  • ❌ 5-10x memory overhead
  • ❌ Cannot deploy to production

The Gap: No tool exists for real-time production monitoring of concurrent safety issues.

Thread-Sentry's Solution

βœ… < 5% performance overhead - Production-ready
βœ… Real-time detection - Immediate alerts
βœ… Precise localization - File, line, thread
βœ… Zero-intrusion - Just replace Mutex type
βœ… Comprehensive - Both deadlock and race detection


πŸš€ Quick Start

Installation

[dependencies]
thread-sentry = "0.1"

Three Ways to Detect Races

1️⃣ Guard Auto-Detection (Simplest)

use thread_sentry::{Mutex, init, report_issues};

fn main() {
    init();
    
    let data = Arc::new(Mutex::new(0u64));
    
    // Just replace std::sync::Mutex β†’ thread_sentry::Mutex
    thread::spawn(|| {
        let mut guard = data.lock();
        *guard = 100;  // Auto-detect + Auto-print
    });
    
    report_issues();
}

2️⃣ SentryField Tracking (Recommended)

use thread_sentry::{Mutex, SentryField, init};

struct SharedData {
    counter: SentryField<u64>,  // Auto-track field access
}

fn main() {
    init();
    
    let data = Arc::new(Mutex::new(SharedData::new()));
    
    thread::spawn(|| {
        let mut guard = data.lock();
        guard.counter.set(100);  // Auto-detect + Auto-print
    });
    
    report_issues();
}

3️⃣ Manual Registration (unsafe code)

use thread_sentry::{RaceDetector, AccessType};

unsafe {
    *raw_ptr = value;
    
    // Manual registration for unsafe code
    RaceDetector::record_access_manual(
        addr, thread_id, AccessType::Write, lock_id, size
    );
}

See USAGE_GUIDE.md for detailed examples.


πŸ“Š Performance

Comparison with Alternatives

Tool Performance Overhead Memory Overhead Production Ready
TSan 500-5000% 5-10x ❌ No
Helgrind 2000-3000% 3-5x ❌ No
Thread-Sentry < 5% < 2x βœ… Yes

Benchmarks

Metric std::sync parking_lot Thread-Sentry
Latency 50ns 38ns (-24%) 48ns (-4%)
Throughput 20M ops/sec 26M ops/sec (+30%) 21M ops/sec (+5%)

Key Finding: Thread-Sentry has only 4% overhead vs std::sync::Mutex.

See Performance Report for detailed benchmarks.


✨ Features

1. Deadlock Detection

Automatically detects circular lock dependencies:

let lock1 = Mutex::new(0);
let lock2 = Mutex::new(0);

// Thread 1: lock1 -> lock2
// Thread 2: lock2 -> lock1
// Thread-Sentry detects the cycle and reports immediately!

Output:

╔══════════════════════════════════════════════════════════╗
β•‘ ⚠️  DEADLOCK DETECTED                                    β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Cycle Length: 2 locks
Lock Chain:
  [1] Lock #1 held by Thread 1
    Backtrace:
      1. main.rs:15 - transfer_money()
      
  [2] Lock #2 held by Thread 2
    Backtrace:
      1. main.rs:22 - process_transaction()

2. Race Condition Detection

Detects unsynchronized concurrent memory access:

// Thread 1: write without lock
x = 100;  // Write, no lock

// Thread 2: read without lock
read x;   // Read, no lock

// Thread-Sentry detects the race condition!

Output:

╔══════════════════════════════════════════════════════════╗
β•‘ ⚑ RACE CONDITION DETECTED                               β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Memory Address: 0x7f8a3c001000

Access 1: Write at bank.rs:30 (Thread 1, no lock)
Access 2: Read at bank.rs:45 (Thread 2, no lock)

πŸ—οΈ Architecture

Thread-Sentry is built on three layers:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        Application Layer                 β”‚
β”‚  SentinelMutex / SentinelRwLock         β”‚  ← Replace standard locks
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚        Monitoring Layer                  β”‚
β”‚  - Lock event tracking                   β”‚  ← Intercept lock/unlock
β”‚  - Thread state management               β”‚
β”‚  - Dependency graph construction         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚        Detection Layer                   β”‚  ← Real-time analysis
β”‚  - Deadlock detector (cycle detection)   β”‚
β”‚  - Race detector (access tracking)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

See Architecture Documentation for details.


πŸ“– Documentation


πŸ”§ Advanced Usage

Selective Monitoring

// Development: Enable full monitoring
#[cfg(debug_assertions)]
use thread_sentry::Mutex;

// Production: Selective monitoring
#[cfg(not(debug_assertions))]
{
    thread_sentry::init();
    // Use Thread-Sentry for critical locks only
    // Use parking_lot for high-frequency paths
}

Custom Configuration

// Initialize with custom settings
thread_sentry::init();

// Use different lock types
let critical_lock = Mutex::new(data);  // Full monitoring
let fast_lock = RwLock::new(cache);    // Read-write monitoring

πŸ§ͺ Testing

Run Tests

cargo test

Run Examples

# Demo
cargo run --example demo

# Benchmark
cargo run --example benchmark

# Real-world scenario
cargo run --example real_world

Performance Testing

# See docs/PERFORMANCE_TESTING_GUIDE.md for details
scripts\run_benchmarks.bat

🀝 Comparison with parking_lot

parking_lot deadlock_detection

Advantages:

  • βœ… < 1% overhead (very low)
  • βœ… Basic deadlock detection

Limitations:

  • ❌ No race condition detection
  • ❌ 10-second polling delay
  • ❌ Limited information (only lock IDs)
  • ❌ Not suitable for CI/CD

Thread-Sentry

Advantages:

  • βœ… Real-time detection (0 delay)
  • βœ… Race condition detection
  • βœ… Precise localization (file, line, thread)
  • βœ… Suitable for development and CI/CD
  • βœ… Production-ready (< 5% overhead)

Trade-off:

  • Higher overhead (6% vs < 1%)

Best Practice

Use both:

  • Development: Thread-Sentry (comprehensive diagnosis)
  • Production: parking_lot (long-term monitoring)
  • Critical paths: Thread-Sentry (safety priority)
  • High-frequency: parking_lot (performance priority)

🎯 Use Cases

Ideal Scenarios

βœ… High-concurrency services

  • Web servers, API endpoints
  • Database connection pools
  • Message queue systems

βœ… Long-running applications

  • Background services
  • Scheduled tasks
  • Event-driven systems

βœ… Critical business logic

  • Financial transactions
  • Inventory management
  • Order processing

βœ… Development and debugging

  • Real-time feedback
  • CI/CD integration
  • Early problem detection

Not Suitable For

  • Single-threaded applications
  • Extremely performance-sensitive scenarios (< 5% overhead unacceptable)
  • Already using other detection tools

πŸ“¦ Examples

Banking System

use thread_sentry::Mutex;
use std::sync::Arc;

struct BankAccount {
    id: u64,
    balance: f64,
}

fn transfer(from: Arc<Mutex<BankAccount>>, to: Arc<Mutex<BankAccount>>, amount: f64) {
    let from_account = from.lock();
    let to_account = to.lock();  // Thread-Sentry checks for deadlock
    
    from_account.balance -= amount;
    to_account.balance += amount;
}

Producer-Consumer

use thread_sentry::Mutex;

let buffer = Arc::new(Mutex::new(Vec::new()));

// Producer
thread::spawn(|| {
    let mut buf = buffer.lock();
    buf.push(item);
});

// Consumer
thread::spawn(|| {
    let mut buf = buffer.lock();
    if let Some(item) = buf.pop() {
        process(item);
    }
});

πŸ” Technical Highlights

Low Overhead Design

  • DashMap: Lock-free concurrent hash map
  • SmallVec: Stack-allocated small arrays
  • Incremental detection: Only check new edges
  • Lazy backtrace: Collect only when issues found

Detection Algorithms

Deadlock:

  • Build dependency graph
  • DFS cycle detection
  • Real-time graph updates

Race Condition:

  • Track memory access history
  • Happens-before analysis
  • Conflict detection

πŸ“ˆ Roadmap

Short-term

  • Async lock support (tokio::sync::Mutex)
  • Statistical sampling (< 1% overhead)
  • Visualization tools (dependency graph SVG)
  • Structured logging output

Long-term

  • Distributed deadlock detection (cross-service)
  • ML-based problem prediction
  • GPU-accelerated graph analysis
  • IDE plugin integration

🀝 Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Development Setup

git clone https://github.com/yourusername/thread-sentry.git
cd thread-sentry
cargo build
cargo test

πŸ“„ License

MIT License


πŸ™ Acknowledgments

Built with amazing Rust community libraries:

  • parking_lot: High-performance lock implementation
  • dashmap: Concurrent hash map
  • crossbeam: Concurrent primitives
  • backtrace: Stack trace collection

πŸ“ž Support


Thread-Sentry: Making concurrent programming safer, one lock at a time. πŸ›‘οΈ

About

High-performance deadlock & race condition detector for Rust with <5% overhead. Production-ready, zero-intrusion monitoring.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors