Async synchronization primitives for the compio runtime.
- Semaphore: Async semaphore for bounding concurrency
- Lock-free fast path using atomics
- FIFO waiter queue for fairness
- RAII permit guards for automatic cleanup
- Compatible with compio's async runtime
Add to your Cargo.toml:
[dependencies]
compio-sync = "0.1"use compio_sync::Semaphore;
use std::sync::Arc;
#[compio::main]
async fn main() {
// Create semaphore with 100 permits
let sem = Arc::new(Semaphore::new(100));
// Spawn 1000 tasks, but only 100 run concurrently
let mut handles = Vec::new();
for i in 0..1000 {
let sem = sem.clone();
let handle = compio::runtime::spawn(async move {
let _permit = sem.acquire().await;
println!("Task {} running (max 100 concurrent)", i);
// Permit automatically released when dropped
});
handles.push(handle);
}
// Wait for all tasks
for handle in handles {
handle.await.unwrap();
}
}impl Semaphore {
/// Create a new semaphore with the given number of permits
pub fn new(permits: usize) -> Self;
/// Acquire a permit, waiting asynchronously if none available
pub async fn acquire(&self) -> SemaphorePermit;
/// Try to acquire a permit without waiting
pub fn try_acquire(&self) -> Option<SemaphorePermit>;
/// Get the number of available permits
pub fn available_permits(&self) -> usize;
/// Get the maximum number of permits (configured limit)
pub fn max_permits(&self) -> usize;
/// Get the number of permits currently in use
pub fn in_use(&self) -> usize;
}The semaphore uses a two-tier approach for optimal performance:
- Fast Path (Lock-Free): When permits are available, acquisition uses atomic compare-and-swap operations with no mutex locking
- Slow Path (Mutex-Protected): When no permits are available, tasks register their waker in a FIFO queue and wait for notification
This design minimizes contention while providing fair scheduling.
- Bounding Concurrency: Limit the number of concurrent file operations
- Resource Protection: Prevent file descriptor or memory exhaustion
- Backpressure: Pause discovery when processing is saturated
- Rate Limiting: Control operation throughput
This semaphore is inspired by tokio::sync::Semaphore but built specifically for the compio runtime:
| Feature | tokio::sync::Semaphore | compio-sync::Semaphore |
|---|---|---|
| Runtime | Tokio | Compio |
| Lock-free fast path | ✅ | ✅ |
| FIFO fairness | ✅ | ✅ |
| RAII permits | ✅ | ✅ |
| Async acquire | ✅ | ✅ |
| Try acquire | ✅ | ✅ |
We are actively researching and planning to eliminate mutexes from our synchronization primitives to achieve truly lock-free async wakeups. This work is documented in the docs/ directory:
- Implementation Plan - Step-by-step guide for implementing lock-free wakeups
- Research Document - Comprehensive analysis of approaches (Tokio, Python asyncio, etc.)
- Visual Comparison - Side-by-side code comparisons and decision matrix
- Docs Index - Complete documentation guide
Phase 1: parking_lot + AtomicWaker (1-2 days)
- Replace
std::sync::Mutexwithparking_lot::Mutex - Add
AtomicWakerfast path for single-waiter scenarios - Expected: 20-30% performance improvement
Phase 2: crossbeam Lock-Free Queue (1 week)
- Replace mutex-based queue with
crossbeam-queue::SegQueue - True lock-free operation
- Expected: 40-60% performance improvement
Phase 3: Intrusive Lists (Optional, 3-4 weeks)
- Tokio-style intrusive linked lists
- Zero allocations, maximum performance
- Only if Phase 2 isn't sufficient
See the research docs for detailed analysis and trade-offs.
Our CI tests across multiple platforms to ensure compatibility and performance:
| Platform | Purpose | Implementation |
|---|---|---|
| Ubuntu 24.04 | Modern Linux (kernel 6.11) | io_uring futex integration |
| Ubuntu 22.04 | Older Linux (kernel 5.15) | Generic fallback (parking_lot) |
| Windows 2022 | Modern Windows | IOCP event integration (planned) |
| macOS 14 | macOS | Generic fallback (parking_lot) |
- Linux: Uses io_uring futex operations for unified event loop
- Windows: Will use IOCP events for unified event loop (Phase 3)
- macOS: Uses generic fallback (kqueue could be added in future)
- Fallback: Generic implementation using parking_lot + AtomicWaker
Windows 7 Support: While compio-sync is designed to work on older Windows versions (including Windows 7), we cannot test this in CI due to GitHub Actions limitations. GitHub Actions only provides:
windows-latest(Windows Server 2022)windows-2019(Windows Server 2019)windows-2016(Windows Server 2016)
Legacy Windows Testing: For Windows 7 and other legacy versions, the library will use the generic fallback implementation (parking_lot + AtomicWaker). This provides good performance while maintaining compatibility with older systems.
This repository includes a pre-commit hook that automatically formats and lints code before commits. To install it:
# Install the pre-commit hook
./scripts/install-pre-commit.shThe hook will:
- Format all Rust code with
cargo fmt - Run clippy lints with strict settings
- Run all tests to ensure nothing is broken
All code must pass:
cargo fmt --all(formatting)cargo clippy --all-targets --all-features -- -D warnings(linting)cargo test --all-targets(testing)
Contributions are welcome! Please see:
- Design documents for architecture and research
- Semaphore Design for current implementation
- Implementation Plan for planned improvements
MIT