Crates.io | unsync |
lib.rs | unsync |
version | 0.1.2 |
source | src |
created_at | 2022-04-13 11:46:22.085651 |
updated_at | 2023-03-22 13:30:06.968374 |
description | Unsynchronized synchronization primitives for async Rust. |
homepage | https://github.com/udoprog/unsync |
repository | https://github.com/udoprog/unsync |
max_upload_size | |
id | 566956 |
size | 114,848 |
Unsynchronized synchronization primitives for async Rust.
This crate provides a fairly simple set of synchronization primitives which
are explicitly !Send
and !Sync
. This makes them useful for use in
singlethreaded systems like yew.
You can think of this as a modern replacement to the now-deprecated futures::unsync module.
!Send
/ !Sync
synchronization primitives?Having unsynchronized sync primitives might seem weird since they are largely used for inter-task communication across threads.
The need for such primitives increase as we are seeing more singlethreaded systems like yew mature and are receiving richer support for async programming. In order to make them as efficient as possible it's useful that they are written with unsynchronized systems and constraints in mind so that they don't have to make use of atomics and locks.
The overhead of synchronization in real systems should be minor because of the role channels play in typical applications and they are optimized for uncontended use. But unsynchronized code still has the ability to optimize better for both performance and size.
In one of my applications replacing the use of tokio::sync::oneshot
with
unsync::oneshot
reduced the size of the resulting binary by 30kb (10kb
when optimized with wasm-opt
). Synthetic benchmarks in this project hints
at the unsync channels being about twice as fast for optimized builds when
used inside of a LocalSet.
I haven't dug too deep into the specifics of why this is, but as long as this is the case I'd like to have access to drop in replacements allowing someone to tap into these benefits.
Add the following to your Cargo.toml
:
unsync = "0.1.1"
use unsync::spsc::{channel, Sender, Receiver};
use std::error::Error;
use tokio::task;
async fn receiver(mut rx: Receiver<u32>) -> Vec<u32> {
let mut out = Vec::new();
while let Some(m) = rx.recv().await {
out.push(m);
}
out
}
async fn sender(mut tx: Sender<u32>) -> Result<(), Box<dyn Error>> {
for n in 0..1000 {
tx.send(n).await?;
}
Ok(())
}
async fn run() -> Result<(), Box<dyn Error>> {
let (tx, rx) = channel(4);
let _ = task::spawn_local(sender(tx));
let out = task::spawn_local(receiver(rx)).await?;
let expected = (0..1000).collect::<Vec<u32>>();
assert_eq!(out, expected);
Ok(())
}