Sack

Struct Sack 

Source
pub struct Sack<T> { /* private fields */ }
Expand description

A lock-free sack data structure.

A sack is a concurrent data structure that allows adding items and draining them in a lock-free manner. It is implemented as a singly-linked list where the head is an atomic pointer. This allows multiple producers to add items concurrently without locks.

§How it works

The Sack is essentially a LIFO (last-in, first-out) stack. When an item is added, it is pushed to the front of the list. When the sack is drained, the entire list is atomically swapped with an empty list, and the old list is returned as a draining iterator.

This design has the following properties:

  • Lock-free: Adding and draining items are lock-free operations, which means they don’t require mutual exclusion. This makes them very fast and scalable.
  • Concurrent producers: Multiple threads can add items to the sack concurrently.
  • Single consumer: Only one thread can drain the sack at a time. This is enforced by the &self receiver on the drain method.

§Example

use sack::Sack;
use std::sync::Arc;
use std::thread;

let sack = Arc::new(Sack::new());

// Spawn a producer thread.
let producer = {
    let sack = Arc::clone(&sack);
    thread::spawn(move || {
        for i in 0..10 {
            sack.add(i);
        }
    })
};

// Wait for the producer to finish.
producer.join().unwrap();

// Drain the sack and collect the items.
let mut items: Vec<_> = sack.drain().collect();
items.sort();

assert_eq!(items, (0..10).collect::<Vec<_>>());

Implementations§

Source§

impl<T> Sack<T>

Source

pub const fn new() -> Self

Creates a new, empty sack.

Source

pub fn add(&self, item: T)

Adds an item to the sack.

This operation is lock-free and can be called by multiple threads concurrently.

Source

pub fn drain(&self) -> Drain<T>

Drains all items from the sack.

This operation is lock-free and returns a draining iterator over the items in the sack.

Source

pub fn is_empty(&self) -> bool

Checks if the sack is empty.

This operation is lock-free.

Trait Implementations§

Source§

impl<T> Default for Sack<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<T> !Freeze for Sack<T>

§

impl<T> RefUnwindSafe for Sack<T>

§

impl<T> Send for Sack<T>

§

impl<T> Sync for Sack<T>

§

impl<T> Unpin for Sack<T>

§

impl<T> UnwindSafe for Sack<T>
where T: RefUnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.