Skip to content

ErikBot42/rendezvous_swap

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

A fast rendezvous in rust where data can optionally be swapped between the two threads.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages