Skip to content

Commit

Permalink
Update docs and README
Browse files Browse the repository at this point in the history
  • Loading branch information
skogseth committed Sep 6, 2024
1 parent 02e0f81 commit 2371056
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 43 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# hzrd
Provides shared, mutable state by utilizing hazard pointers.

The core concept of the crate is to trade memory for speed. The containers avoid locking the value, and instead accumulate garbage: Excess data that will need to be freed at a later point. The garbage collection is controlled using hazard pointers. Each reader of the value can hold one reference to the value. If the value of the container is swapped, then the reference they hold is kept valid through their hazard pointer. They can then (at some later point) drop the reference, and the value will be cleaned up _at some point_. The core API in the crate is the HzrdCell.

## HzrdCell

HzrdCell aims to provide something akin to a multithreaded version of std's Cell-type. A basic example:
The core API of this crate is the HzrdCell, which provides an API reminiscent to that of the standard library's Cell-type. However, HzrdCell allows shared mutation across multiple threads.

```rust
use hzrd::HzrdCell;
Expand All @@ -14,12 +12,12 @@ let cell = HzrdCell::new(false);

std::thread::scope(|s| {
s.spawn(|| {
// Loop until the value is true
// Loop until the value is true ...
while !cell.get() {
std::hint::spin_loop();
}

// And then set it back to false!
// ... and then set it back to false!
cell.set(false);
});

Expand All @@ -33,5 +31,3 @@ std::thread::scope(|s| {
});
});
```

HzrdCell provides memory safe, multithreaded, shared mutability. But this isn't all that useful. We often want some sort of synchronization to avoid races (not data races, just general races).
11 changes: 1 addition & 10 deletions src/domains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ static GLOBAL_DOMAIN: SharedDomain = SharedDomain::new();
/**
A globally shared, multithreaded domain
This is the default domain used by `HzrdCell`, and is the recommended domain for most applications. It's based on a set of globally shared, static variables, and so there is no "constructor" for this domain. The [`GlobalDomain`] struct is a Zero Sized Type (ZST) that acts simply as an accessor to these globally shared variables.
This is the default domain used by `HzrdCell`, and is the recommended domain for most applications. It's based on a 'globally shared, static variable, and so there is no "constructor" for this domain. The [`GlobalDomain`] struct is a Zero Sized Type (ZST) that acts simply as an accessor to this globally shared variable.
# Example
```
Expand Down Expand Up @@ -230,12 +230,6 @@ unsafe impl Domain for GlobalDomain {
fn reclaim(&self) -> usize {
GLOBAL_DOMAIN.reclaim()
}

// -------------------------------------

fn retire(&self, ret_ptr: RetiredPtr) -> usize {
GLOBAL_DOMAIN.retire(ret_ptr)
}
}

impl std::fmt::Debug for GlobalDomain {
Expand Down Expand Up @@ -332,8 +326,6 @@ impl SharedDomain {

unsafe impl Domain for SharedDomain {
fn hzrd_ptr(&self) -> &HzrdPtr {
// Important to only grab shared references to the HzrdPtr's
// as others may be looking at them
match self.hzrd_ptrs.iter().find_map(|node| node.try_acquire()) {
Some(hzrd_ptr) => hzrd_ptr,
None => self.hzrd_ptrs.push_get(HzrdPtr::new()),
Expand All @@ -353,7 +345,6 @@ unsafe impl Domain for SharedDomain {
return 0;
}

std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
let hzrd_ptrs = HzrdPtrs::load(self.hzrd_ptrs.iter());
let remaining: SharedStack<RetiredPtr> = retired_ptrs
.into_iter()
Expand Down
44 changes: 18 additions & 26 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,38 @@
/*!
This crate provides a safe API for shared mutability using hazard pointers for memory reclamation.
# Hazard pointers
Hazard pointers is a strategy for controlled memory reclamation in multithreaded contexts. All readers/writers have shared access to some data, as well as a collection of garbage, and a list of hazard pointers. Whenever you read the value of the data, you actually get a reference to it, which you also store in one of the hazard pointers. Writing to the data is done by swapping out the old value for a new one; the old value is then "retired" (thrown in the pile of garbage). Retired values are only reclaimed if no hazard pointers contain their address, in this way the hazard pointers end up protecting values read from becoming invalid.
# HzrdCell
The core API of this crate is the [`HzrdCell`], which provides an API reminiscent to that of the standard library's [`Cell`](std::cell::Cell)-type. However, [`HzrdCell`] allows shared mutation across multiple threads.
The main advantage of [`HzrdCell`], compared to something like a [`Mutex`](std::sync::Mutex), is that reading and writing to the value is lock-free. This is offset by an increased memory use, an significant overhead and additional indirection. Here is an example of [`HzrdCell`] in use.
```
use std::time::Duration;
use std::thread;
use hzrd::HzrdCell;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Idle,
Running,
Finished,
}
let state = HzrdCell::new(State::Idle);
let cell = HzrdCell::new(false);
thread::scope(|s| {
std::thread::scope(|s| {
s.spawn(|| {
thread::sleep(Duration::from_millis(1));
match state.get() {
State::Idle => println!("Waiting is boring, ugh"),
State::Running => println!("Let's go!"),
State::Finished => println!("We got here too late :/"),
// Loop until the value is true
while !cell.get() {
std::hint::spin_loop();
}
// And then set it back to false!
cell.set(false);
});
s.spawn(|| {
state.set(State::Running);
thread::sleep(Duration::from_millis(1));
state.set(State::Finished);
// Set the value to true
cell.set(true);
// And then read the value!
// This might print either `true` or `false`
println!("{}", cell.get());
});
});
assert_eq!(state.get(), State::Finished);
```
*/

Expand All @@ -55,8 +49,6 @@ pub use crate::domains::{GlobalDomain, LocalDomain, SharedDomain};

mod private {
// We want to test the code in the readme
//
// TODO: Data race in the README (yeah, for real)
#![doc = include_str!("../README.md")]
}

Expand Down

0 comments on commit 2371056

Please sign in to comment.