Skip to main content

Crate fett

Crate fett 

Source
Expand description

This is the way to do concurrent memoizing maps.

§Examples

A simple memoizing “x + 1” map:

// The constructor accepts a function or closure which we call the "value constructor".
// It takes a reference to a key and returns a new value associated with that key.
let fett = Fett::new(|x| *x + 1);

// Getting a key will call the value constructor the first time that key is accessed.
assert_eq!(fett.get(3), 4);
assert_eq!(fett.get(12), 13);

A basic file cache:

// The value constructor returns file contents as `Arc<str>`, panicking on I/O errors.
let fett = Fett::new(|path| {
    let content = std::fs::read_to_string(path).expect("File must be readable");
    Arc::<str>::from(content)
});

// Assuming `echo 'some contents' >/tmp/some_file`
assert_eq!(&*fett.get("/tmp/some_file"), "some contents");

Caching behavior:

// Demonstrate the caching ability with a counter.
let counter = Cell::new(0);

let fett = Fett::new(|x| {
    counter.set(counter.get() + 1);
    *x + 1
});

// The value constructor is only called once for each unique key.
assert_eq!(counter.get(), 0);
assert_eq!(fett.get(3), 4);
assert_eq!(fett.get(3), 4);
assert_eq!(counter.get(), 1);

// We'll call the value constructor only one additional time.
assert_eq!(fett.get(12), 13);
assert_eq!(fett.get(12), 13);
assert_eq!(fett.get(3), 4);
assert_eq!(counter.get(), 2);

Thread safe and robust:

let counter = AtomicU8::new(0);

let fett = Fett::new(|_| {
    // Increment the counter on each call, and return its old value.
    counter.fetch_add(1, Ordering::Relaxed)
});

// Use the rayon crate to attack our poor cache with many threads.
[0_i32; 32].par_iter().for_each(|_| {
    assert_eq!(fett.get(0), 0);
});

assert_eq!(counter.load(Ordering::Relaxed), 1);

§Deadlock

Unlike most other concurrent map implementations, Fett can only deadlock if the value constructor never returns. Common causes would include infinite loops, I/O requests missing a timeout, resource requests that are busy forever, etc.

Because there is only a single value constructor, this issue is very controllable. Threads competing for values within the map can otherwise never lead to deadlock as there are no mutual dependencies between them.

Fett affords this capability by requiring that the values it stores implements Clone. For this reason, it should only store Copy types or non-Copy types that are wrapped in Arc.

For instance, if you would typically store String values, consider Arc<str> instead. This makes cloning the value inexpensive while remaining thread-safe.

Structs§

Fett
A concurrent HashMap implementation with lazy value construction.