Expand description

StateHashTable solves the rendezvous problem.

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use sync42::state_hash_table::{Handle, Key, Value, StateHashTable};

#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
struct SampleKey {
    key: u64,
}

impl SampleKey {
    const fn new(key: u64) -> Self {
        Self {
            key,
        }
    }
}

impl Key for SampleKey {
}

#[derive(Debug, Default)]
struct SampleValue {
    finished: AtomicBool,
}

impl From<SampleKey> for SampleValue {
    fn from(key: SampleKey) -> Self {
        Self {
            finished: AtomicBool::default(),
        }
    }
}

impl Value for SampleValue {
    fn finished(&self) -> bool { self.finished.load(Ordering::Relaxed) }
}

// Create the state hash table.  This should be a global-ish structure.
let mut sht: StateHashTable<SampleKey, SampleValue> = StateHashTable::new();
// Everything revolves around the key.  We don't demonstrate this, but different keys are
// totally partitioned and do not interact except to contend on a shared lock.
const KEY: SampleKey = SampleKey::new(42);

// There's nothing there until we create it.
assert!(sht.get_state(KEY).is_none());
let mut state1 = sht.create_state(KEY);
assert!(state1.is_some());
let mut state1 = state1.unwrap();

// Attempts to create twice fail with None.
let mut state2 = sht.create_state(KEY);
assert!(state2.is_none());

// But get_state will work.
let mut state3 = sht.get_state(KEY);
assert!(state3.is_some());
let mut state3 = state3.unwrap();

// It is guaranteed that when two threads hold reference to the same hash table and have [Eq]
// keys they will be the same underlying value.

Handle::is_same(&state1, &state3);

// It is also guaranteed that when state is dropped but the work is unfinished that the value
// will persist in the table.  Note that there will be no handles to this state and it will
// persist.
drop(state1);
drop(state3);

// Notice that we use [get_state] here.  It uses the existing state.
let mut state4 = sht.get_state(KEY);
assert!(state4.is_some());
let mut state4 = state4.unwrap();
state4.finished.store(true, Ordering::Relaxed);

// Drop the remaining references.
drop(state4);

// Get state fails because we marked it finished and dropped all references.  Only when the
// last reference is dropped will the item be collected, even if the outcome of the
// [finished()] call changes.
let mut state5 = sht.get_state(KEY);
assert!(state5.is_none());

Structs§

  • A Handle holds a reference to a key-value pair in a table. Two handles that come from the same table and key are guaranteed to refer to the same piece of state.
  • StateHashTable is the main collection.

Traits§

  • A key for a state hash table.
  • A value for a state hash table.

Functions§