Crates.io | cupchan |
lib.rs | cupchan |
version | 0.1.2 |
source | src |
created_at | 2022-03-19 03:39:17.097211 |
updated_at | 2022-03-20 23:25:41.998798 |
description | Simple async overwriting channel between two threads that is wait & block free by swapping cups around |
homepage | |
repository | https://github.com/zyansheep/cupchan |
max_upload_size | |
id | 552996 |
size | 20,035 |
Yes cup-chan, please swap my cups around uwu
Simple async overwriting channel between two threads that is wait & block free by swapping cups around
This project came from the need for me to have a thread lazily update some data on another thread without having to wait for mutexes.
the way this crate accomplishes a wait/block-free channel is by having three "cups" with swappable labels. Each cup is marked as having a specific purpose i.e. writing, storage, and reading. (This marker is stored in an atomic u8).
The writing thread has access to the writing cup, and the reading thread has access to the cup marked as reading. Once the writing thread is ready to update the reading thread, it writes to its cup and calls flush()
which switches the writing and storage markers around. (this is a single atomic operation using fetch_update
).
For example, the cups could start out like this: <W><S><R>
The writer thread writes something: <S><W><R>
- the writer marker has now swaped with the storage marker and a flag is set to tell the reader thread that the storage was updated.
The reader thread wants to check the data, so it checks the update flag. If set, the reader swaps the storage and reader markers. Then the reader looks inside the reader cup for the data.
The system of markers ensures that the writing and reading thread never access the same cup at the same time.
Here is a diagram of all the possible cup states and the relations between them: quiver.
This crate has been validated with loom
Run tests:
$ cargo test
$ RUSTFLAGS="--cfg loom" cargo test --test loom_test --release
Note to self: If using LOOM flags, make sure to clear checkpoint file after changing code.
Each benchmark represents 5_000 64-bit integers being sent.
test tests::bench_crossbeam_chan_cap_10 ... bench: 227,330 ns/iter (+/- 53,862)
test tests::bench_crossbeam_chan_cap_3 ... bench: 406,431 ns/iter (+/- 73,856)
test tests::bench_cupchan_greedy ... bench: 404,699 ns/iter (+/- 36,296)
test tests::bench_cupchan_lazy ... bench: 102,177 ns/iter (+/- 10,770)
test tests::bench_flume_chan ... bench: 725,894 ns/iter (+/- 107,237)
This crate is faster than crossbeam & flume for what it is supposed to do (stream updated values lazily). If you just need to move and consume data as fast as possible, use those crates instead.
(The difference between lazy & greedy is that lazy yields the thread after every read).
It still is not as fast as it could be, mostly because of the use of fetch_update
instead of cpu intrinsics, if anyone has an idea for how to make this better, ping me on the rust discord (i go by @Zyansheep#8020
).