| Crates.io | lite-sync |
| lib.rs | lite-sync |
| version | 0.2.3 |
| created_at | 2025-11-04 19:12:22.804956+00 |
| updated_at | 2026-01-11 16:14:39.802449+00 |
| description | Fast, lightweight async primitives: SPSC channel, oneshot, notify, and atomic waker |
| homepage | https://github.com/ShaoG-R/lite-sync |
| repository | https://github.com/ShaoG-R/lite-sync |
| max_upload_size | |
| id | 1916829 |
| size | 253,967 |
Fast, lightweight async primitives: SPSC channel, oneshot, notify, and atomic waker.
π English | π δΈζζζ‘£
lite-sync provides a collection of optimized synchronization primitives designed for low latency and minimal allocations. These primitives are built from the ground up with performance in mind, offering alternatives to heavier standard library implementations.
no_std environments (requires alloc)Add this to your Cargo.toml:
[dependencies]
lite-sync = "0.2"
lite-sync supports no_std environments (requires alloc). Disable default features in Cargo.toml:
[dependencies]
lite-sync = { version = "0.2", default-features = false }
oneshotOne-shot channel for sending a single value between tasks, with API behavior aligned with tokio::sync::oneshot.
Provides two variants:
oneshot::generic - For arbitrary types T: Send, uses UnsafeCell<MaybeUninit<T>> for storageoneshot::lite - Ultra-lightweight variant for State-encodable types, uses only AtomicU8 for storageAPI (aligned with tokio oneshot):
channel<T>() - Create a sender/receiver pairSender::send(value) -> Result<(), T> - Send value, returns Err(value) if receiver is closedSender::is_closed() - Check if receiver has been dropped or closedReceiver::recv().await / receiver.await - Async receive, returns Result<T, RecvError>Receiver::try_recv() - Non-blocking receive, returns Result<T, TryRecvError>Receiver::close() - Close the receiver, preventing future sendsReceiver::blocking_recv() - Blocking receive for synchronous codeNote: Unlike tokio's oneshot which uses CAS to guarantee
Errwhen receiver is already closed, our implementation usesArcrefcount check for simplicity. IfsendandReceiverdrop occur concurrently,sendmay returnOk(())even if the value will not be received. UseReceiver::close()for explicit cancellation when guaranteed detection is needed.
Key features:
Future implementation for ergonomic .awaitblocking_recv) and async usagespscHigh-performance async SPSC (Single Producer Single Consumer) channel.
Built on smallring for efficient ring buffer operations with inline storage support. Type-safe enforcement of single producer/consumer semantics eliminates synchronization overhead.
Key optimizations:
UnsafeCellnotifyLightweight single-waiter notification primitive.
Much lighter than tokio::sync::Notify when you only need to wake one task at a time. Ideal for internal synchronization in other primitives.
atomic_wakerAtomic waker storage with state machine synchronization.
Based on Tokio's AtomicWaker but simplified for specific use cases. Provides safe concurrent access to a waker without Box allocation.
use lite_sync::oneshot::generic::{channel, Sender, Receiver, RecvError, TryRecvError};
#[tokio::main]
async fn main() {
// Create a channel for any Send type
let (tx, rx) = channel::<String>();
tokio::spawn(async move {
// send() returns Err(value) if receiver is closed
if tx.send("Hello".to_string()).is_err() {
println!("Receiver dropped");
}
});
// Direct .await or use recv()
match rx.await {
Ok(msg) => println!("Received: {}", msg),
Err(RecvError) => println!("Sender dropped"),
}
}
use lite_sync::oneshot::generic::channel;
#[tokio::main]
async fn main() {
let (tx, mut rx) = channel::<i32>();
// Check if receiver is closed
assert!(!tx.is_closed());
// Close the receiver - prevents future sends
rx.close();
assert!(tx.is_closed());
// send() fails after close
assert!(tx.send(42).is_err());
}
use lite_sync::oneshot::lite::{State, Sender};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TaskResult {
Success,
Error,
}
impl State for TaskResult {
fn to_u8(&self) -> u8 {
match self {
TaskResult::Success => 1,
TaskResult::Error => 2,
}
}
fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(TaskResult::Success),
2 => Some(TaskResult::Error),
_ => None,
}
}
fn pending_value() -> u8 { 0 }
fn closed_value() -> u8 { 255 }
fn receiver_closed_value() -> u8 { 254 }
}
#[tokio::main]
async fn main() {
let (sender, receiver) = Sender::<TaskResult>::new();
tokio::spawn(async move {
sender.send(TaskResult::Success).unwrap();
});
match receiver.await {
Ok(TaskResult::Success) => println!("Task succeeded"),
Ok(TaskResult::Error) => println!("Task failed"),
Err(_) => println!("Sender dropped"),
}
}
use lite_sync::spsc::channel;
use std::num::NonZeroUsize;
#[tokio::main]
async fn main() {
// Channel with capacity 32, inline buffer size 8
let (tx, rx) = channel::<i32, 8>(NonZeroUsize::new(32).unwrap());
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
let mut sum = 0;
while let Some(value) = rx.recv().await {
sum += value;
}
assert_eq!(sum, 45); // 0+1+2+...+9
}
use lite_sync::notify::SingleWaiterNotify;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let notify = Arc::new(SingleWaiterNotify::new());
let notify_clone = notify.clone();
tokio::spawn(async move {
// Do some work...
notify_clone.notify_one();
});
notify.notified().await;
}
use lite_sync::oneshot::lite::Sender;
#[tokio::main]
async fn main() {
let (sender, receiver) = Sender::<()>::new();
tokio::spawn(async move {
sender.send(()).unwrap();
});
match receiver.await {
Ok(()) => println!("Task completed"),
Err(_) => println!("Sender dropped"),
}
}
use lite_sync::oneshot::generic::channel;
fn main() {
let (tx, rx) = channel::<String>();
std::thread::spawn(move || {
tx.send("Hello from thread".to_string()).unwrap();
});
// blocking_recv() for synchronous code
match rx.blocking_recv() {
Ok(msg) => println!("Received: {}", msg),
Err(_) => println!("Sender dropped"),
}
}
Performance benchmarks are available in the benches/ directory. Run them with:
cargo bench
Key characteristics:
All primitives use unsafe internally for performance but expose safe APIs. Safety is guaranteed through:
Clone on SPSC endpoints)Rust 2024 edition (Rust 1.85.0 or later)
Contributions are welcome! Please feel free to submit a Pull Request.
Licensed under either of:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.