rendezvous_swap

Crates.iorendezvous_swap
lib.rsrendezvous_swap
version0.1.0
sourcesrc
created_at2023-03-16 22:04:52.725503
updated_at2023-03-16 22:04:52.725503
descriptionSwap data and sync execution between a pair of threads efficiently
homepage
repositoryhttps://github.com/ErikBot42/rendezvous_swap
max_upload_size
id812112
size55,890
Erik (ErikBot42)

documentation

README

rendezvous_swap

A rendezvous is an execution barrier between a pair of threads, but this crate also provides the option of swapping data at the synchronisation point. (Terminology is from The Little Book of Semaphores)

This is mainly intended for situations where threads sync frequently. Unlike a normal spinlock, it does not use any CAS instructions, just [Acquire] loads and [Release] stores which means it can compile to just a handful of non atomic instructions on x86_64. Because the crate uses atomics for synchronisation, it is also no_std.

Data is internally swapped with pointers, so large structures are not costly to swap and therefore do not need to be boxed.

In microbenchmarks on a i5-7200U CPU, it takes less than 100 ns to swap data.

Safety

[RendezvousData] contains unsafe but all tests pass when running with Miri.

Example: Sync thread execution

use rendezvous_swap::Rendezvous;
use std::thread;

let (mut my_rendezvous, mut their_rendezvous) = Rendezvous::new();
thread::spawn(move || {
    for i in 1..5 {
        println!("{i}");
        their_rendezvous.wait();
    }
});
for i in 1..5 {
    println!("{i}");
    my_rendezvous.wait();
}

This prints:

1
1
2
2
3
3
4
4

Example: Swap thread data

use std::thread;
use rendezvous_swap::RendezvousData;

let (mut my_rendezvous, mut their_rendezvous) = RendezvousData::new(0, 0);
let handle = thread::spawn(move || {
    let borrow = their_rendezvous.swap();
    *borrow = 3;

    let borrow = their_rendezvous.swap();
    assert_eq!(7, *borrow);
});
let borrow = my_rendezvous.swap();
*borrow = 7;

let borrowed_data = my_rendezvous.swap();
assert_eq!(3, *borrowed_data);

Example: Safety

The following won't compile due to the limited lifetime of the references provided by [RendezvousData::swap], you will get the familiar lifetime errors as if you are borrowing a struct element. This crate is safe because it is not possible for both threads to have mutable references to the same memory location at the same time.

use std::thread;
use rendezvous_swap::RendezvousData;

let (mut my_rendezvous, mut their_rendezvous) = RendezvousData::new(0, 0);
let handle = thread::spawn(move || {
    their_rendezvous.swap(); // swap return values can be ignored
    their_rendezvous.swap();
});
let old_borrow = my_rendezvous.swap(); // first mutable borrow occurs here

let new_borrow = my_rendezvous.swap(); // second mutable borrow occurs here

*old_borrow = 3; // first borrow is later used here

Current version: 0.1.0

License: GPL-3.0

Commit count: 15

cargo fmt