boxcar-0.2.7/.cargo_vcs_info.json0000644000000001360000000000100123200ustar { "git": { "sha1": "b9b6f57a355c0e9437d50cbdf567902fc2b59713" }, "path_in_vcs": "" }boxcar-0.2.7/Cargo.toml0000644000000023040000000000100103150ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "boxcar" version = "0.2.7" authors = ["Ibraheem Ahmed "] build = false exclude = [ ".gitignore", ".github/**", "report.svg", ] autobins = false autoexamples = false autotests = false autobenches = false description = "A concurrent, append-only vector" readme = "README.md" keywords = [ "concurrent", "vector", "lock-free", ] categories = [ "concurrency", "data-structures", ] license = "MIT" repository = "https://github.com/ibraheemdev/boxcar" [lib] name = "boxcar" path = "src/lib.rs" [[test]] name = "vec" path = "tests/vec.rs" [[bench]] name = "bench" path = "benches/bench.rs" harness = false [dependencies] [dev-dependencies.criterion] version = "0.3.5" boxcar-0.2.7/Cargo.toml.orig000064400000000000000000000010031046102023000137710ustar 00000000000000[package] name = "boxcar" version = "0.2.7" authors = ["Ibraheem Ahmed "] edition = "2021" license = "MIT" readme = "README.md" description = "A concurrent, append-only vector" repository = "https://github.com/ibraheemdev/boxcar" keywords = ["concurrent","vector","lock-free"] categories = ["concurrency", "data-structures"] exclude = [ ".gitignore", ".github/**", "report.svg", ] [[bench]] name = "bench" harness = false [dependencies] # ... [dev-dependencies] criterion = "0.3.5" boxcar-0.2.7/LICENSE000064400000000000000000000020571046102023000121210ustar 00000000000000MIT License Copyright (c) 2022 Ibraheem Ahmed Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. boxcar-0.2.7/README.md000064400000000000000000000034131046102023000123700ustar 00000000000000# `boxcar` [crates.io](https://crates.io/crates/boxcar) [github](https://github.com/ibraheemdev/boxcar) [docs.rs](https://docs.rs/boxcar) A concurrent, append-only vector. The vector provided by this crate suports concurrent `get` and `push` operations. All operations are lock-free. ## Examples Appending an element to a vector and retrieving it: ```rust let vec = boxcar::Vec::new(); vec.push(42); assert_eq!(vec[0], 42); ``` The vector can be shared across threads with an `Arc`: ```rust use std::sync::Arc; fn main() { let vec = Arc::new(boxcar::Vec::new()); // spawn 6 threads that append to the vec let threads = (0..6) .map(|i| { let vec = vec.clone(); std::thread::spawn(move || { vec.push(i); // push through `&Vec` }) }) .collect::>(); // wait for the threads to finish for thread in threads { thread.join().unwrap(); } for i in 0..6 { assert!(vec.iter().any(|(_, &x)| x == i)); } } ``` Elements can be mutated through fine-grained locking: ```rust use std::sync::{Mutex, Arc}; fn main() { let vec = Arc::new(boxcar::Vec::new()); // insert an element vec.push(Mutex::new(1)); let thread = std::thread::spawn({ let vec = vec.clone(); move || { // mutate through the mutex *vec[0].lock().unwrap() += 1; } }); thread.join().unwrap(); let x = vec[0].lock().unwrap(); assert_eq!(*x, 2); } ``` boxcar-0.2.7/benches/bench.rs000064400000000000000000000102521046102023000141440ustar 00000000000000// adapted from: https://github.com/hawkw/sharded-slab/blob/main/benches/bench.rs // which carries the following MIT license: // // Copyright (c) 2019 Eliza Weisman // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use std::{ sync::{Arc, Barrier, RwLock}, thread, time::{Duration, Instant}, }; #[derive(Clone)] struct Harness { start: Arc, end: Arc, vec: Arc, } const THREADS: usize = 12; impl Harness where T: Send + Sync + 'static, { fn new(vec: Arc) -> Self { Self { start: Arc::new(Barrier::new(THREADS + 1)), end: Arc::new(Barrier::new(THREADS + 1)), vec, } } fn run(&self, f: impl FnOnce(&T) + Send + Copy + 'static) -> Duration { for _ in 0..THREADS { let start = self.start.clone(); let end = self.end.clone(); let vec = self.vec.clone(); thread::spawn(move || { start.wait(); f(&*vec); end.wait(); }); } self.start.wait(); let t0 = Instant::now(); self.end.wait(); t0.elapsed() } } fn write_read(c: &mut Criterion) { const WORKLOAD: &[usize] = &[100, 1000, 10_000, 50_000, 100_000]; let mut group = c.benchmark_group("push_get"); group.measurement_time(Duration::from_secs(15)); for i in WORKLOAD { group.bench_with_input(BenchmarkId::new("boxcar::Vec<_>", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = Harness::new(Arc::new(boxcar::Vec::new())); let elapsed = bench.run(move |vec: &boxcar::Vec| { let v: Vec<_> = (0..i).map(|_| vec.push(true)).collect(); for i in v { assert!(vec.get(i).unwrap()); } }); total += elapsed; } total }) }); group.bench_with_input(BenchmarkId::new("RwLock>", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = Harness::new(Arc::new(RwLock::new(Vec::new()))); let elapsed = bench.run(move |vec: &RwLock>| { let v: Vec<_> = (0..i) .map(|_| { let mut vec = vec.write().unwrap(); vec.push(true); vec.len() - 1 }) .collect(); for i in v { assert!(vec.read().unwrap().get(i).unwrap()); } }); total += elapsed; } total }) }); } group.finish(); } criterion_group!(benches, write_read); criterion_main!(benches); boxcar-0.2.7/src/lib.rs000064400000000000000000000276651046102023000130330ustar 00000000000000#![doc = include_str!("../README.md")] #![deny(unsafe_op_in_unsafe_fn)] #![allow(clippy::needless_doctest_main)] #![no_std] extern crate alloc; mod raw; use core::fmt; use core::ops::Index; /// Creates a [`Vec`] containing the given elements. /// /// `vec!` allows `Vec`s to be defined with the same syntax as array expressions. /// There are two forms of this macro: /// /// - Create a [`Vec`] containing a given list of elements: /// /// ``` /// let vec = vec![1, 2, 3]; /// assert_eq!(vec[0], 1); /// assert_eq!(vec[1], 2); /// assert_eq!(vec[2], 3); /// ``` /// /// - Create a [`Vec`] from a given element and size: /// /// ``` /// let vec = vec![1; 3]; /// assert_eq!(vec, [1, 1, 1]); /// ``` #[macro_export] macro_rules! vec { () => { $crate::Vec::new() }; ($elem:expr; $n:expr) => {{ let n = $n; let mut vec = $crate::Vec::with_capacity(n); let iter = ::core::iter::Iterator::take(::core::iter::repeat($elem), n); ::core::iter::Extend::extend(&mut vec, iter); vec }}; ($($x:expr),+ $(,)?) => ( <$crate::Vec<_> as ::core::iter::FromIterator<_>>::from_iter([$($x),+]) ); } /// A concurrent, append-only vector. /// /// See [the crate documentation](crate) for details. /// /// # Notes /// /// The bucket array is stored inline, meaning that the /// `Vec` is quite large. It is expected that you /// store it behind an [`Arc`](std::sync::Arc) or similar. pub struct Vec { raw: raw::Vec, } impl Default for Vec { fn default() -> Vec { Vec::new() } } impl Vec { /// Constructs a new, empty `Vec`. /// /// # Examples /// /// ``` /// let vec: boxcar::Vec = boxcar::Vec::new(); /// ``` pub const fn new() -> Vec { Vec { raw: raw::Vec::EMPTY, } } /// Constructs a new, empty `Vec` with the specified capacity. /// /// The vector will be able to hold at least `capacity` elements /// without reallocating. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::with_capacity(10); /// /// for i in 0..10 { /// // will not allocate /// vec.push(i); /// } /// /// // may allocate /// vec.push(11); /// ``` pub fn with_capacity(capacity: usize) -> Vec { Vec { raw: raw::Vec::with_capacity(capacity), } } /// Clears the vector, removing all values. /// /// Note that this method has no effect on the allocated capacity /// of the vector.ace from the `self`. /// /// # Examples /// ``` /// let mut vec = boxcar::Vec::new(); /// vec.push(1); /// vec.push(2); /// /// vec.clear(); /// assert!(vec.is_empty()); /// /// vec.push(3); // Will not allocate. /// ``` pub fn clear(&mut self) { self.raw.clear(); } /// Reserves capacity for at least `additional` more elements to be inserted /// in the given `Vec`. The collection may reserve more space to avoid /// frequent reallocations. /// /// Does nothing if capacity is already sufficient. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// vec.reserve(10); /// /// for i in 0..10 { /// // will not allocate /// vec.push(i); /// } /// /// // may allocate /// vec.push(11); /// ``` pub fn reserve(&self, additional: usize) { self.raw.reserve(additional) } /// Appends an element to the back of the vector, /// returning the index it was inserted into. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2]; /// assert_eq!(vec.push(3), 2); /// assert_eq!(vec, [1, 2, 3]); /// ``` pub fn push(&self, value: T) -> usize { self.raw.push(value) } /// Appends the element returned from the closure `f` to the back of the vector /// at the index supplied to the closure. /// /// Returns the index that the element was inserted into. /// # Examples /// /// ``` /// let vec = boxcar::vec![0, 1]; /// vec.push_with(|index| index); /// assert_eq!(vec, [0, 1, 2]); /// ``` pub fn push_with(&self, f: F) -> usize where F: Fn(usize) -> T, { self.raw.push_with(f) } /// Returns the number of elements in the vector. /// /// Note that due to concurrent writes, it is not guaranteed /// that all elements `0..vec.count()` are initialized. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// assert_eq!(vec.count(), 0); /// vec.push(1); /// vec.push(2); /// assert_eq!(vec.count(), 2); /// ``` #[inline] pub fn count(&self) -> usize { self.raw.count() } /// Returns `true` if the vector contains no elements. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// assert!(vec.is_empty()); /// /// vec.push(1); /// assert!(!vec.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.count() == 0 } /// Returns a reference to the element at the given index. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![10, 40, 30]; /// assert_eq!(Some(&40), vec.get(1)); /// assert_eq!(None, vec.get(3)); /// ``` pub fn get(&self, index: usize) -> Option<&T> { self.raw.get(index) } /// Returns a mutable reference to the element at the given index. /// /// # Examples /// /// ``` /// let mut vec = boxcar::vec![10, 40, 30]; /// assert_eq!(Some(&mut 40), vec.get_mut(1)); /// assert_eq!(None, vec.get_mut(3)); /// ``` pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { self.raw.get_mut(index) } /// Returns a reference to an element, without doing bounds /// checking or verifying that the element is fully initialized. /// /// For a safe alternative see [`get`](Vec::get). /// /// # Safety /// /// Calling this method with an out-of-bounds index, or for an element that /// is being concurrently initialized is **undefined behavior**, even if /// the resulting reference is not used. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2, 4]; /// /// unsafe { /// assert_eq!(vec.get_unchecked(1), &2); /// } /// ``` pub unsafe fn get_unchecked(&self, index: usize) -> &T { // Safety: Guaranteed by caller. unsafe { self.raw.get_unchecked(index) } } /// Returns a mutable reference to an element, without doing bounds /// checking or verifying that the element is fully initialized. /// /// For a safe alternative see [`get`](Vec::get). /// /// # Safety /// /// Calling this method with an out-of-bounds index is **undefined /// behavior**, even if the resulting reference is not used. /// /// # Examples /// /// ``` /// let mut vec = boxcar::vec![1, 2, 4]; /// /// unsafe { /// assert_eq!(vec.get_unchecked_mut(1), &mut 2); /// } /// ``` pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { // Safety: Guaranteed by caller. unsafe { self.raw.get_unchecked_mut(index) } } /// Returns an iterator over the vector. /// /// Values are yielded in the form `(index, value)`. The vector may /// have in-progress concurrent writes that create gaps, so `index` /// may not be strictly sequential. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2, 4]; /// let mut iterator = vec.iter(); /// /// assert_eq!(iterator.next(), Some((0, &1))); /// assert_eq!(iterator.next(), Some((1, &2))); /// assert_eq!(iterator.next(), Some((2, &4))); /// assert_eq!(iterator.next(), None); /// ``` pub fn iter(&self) -> Iter<'_, T> { Iter { vec: &self.raw, raw: self.raw.iter(), } } } impl Index for Vec { type Output = T; fn index(&self, index: usize) -> &Self::Output { &self.raw[index] } } impl IntoIterator for Vec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { raw: self.raw.iter(), vec: self.raw, } } } impl<'a, T> IntoIterator for &'a Vec { type Item = (usize, &'a T); type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator that moves out of a vector. /// /// This struct is created by the `into_iter` method on [`Vec`] /// (provided by the [`IntoIterator`] trait). pub struct IntoIter { vec: raw::Vec, raw: raw::Iter, } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { self.raw.next_owned(&mut self.vec) } fn size_hint(&self) -> (usize, Option) { let remaining = self.vec.count() - self.raw.yielded(); (remaining, Some(remaining)) } } /// An iterator over the elements of a [`Vec`]. /// /// See [`Vec::iter`] for details. pub struct Iter<'a, T> { vec: &'a raw::Vec, raw: raw::Iter, } impl<'a, T> Clone for Iter<'a, T> { fn clone(&self) -> Iter<'a, T> { Iter { vec: self.vec, raw: self.raw.clone(), } } } impl<'a, T> Iterator for Iter<'a, T> { type Item = (usize, &'a T); fn next(&mut self) -> Option { self.raw.next_shared(self.vec) } fn size_hint(&self) -> (usize, Option) { (self.vec.count() - self.raw.yielded(), None) } } impl<'a, T: fmt::Debug> fmt::Debug for Iter<'a, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Contents<'a, T>(&'a T); impl fmt::Debug for Contents<'_, T> where T: Iterator + Clone, T::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.0.clone()).finish() } } f.debug_tuple("Iter").field(&Contents(self)).finish() } } impl FromIterator for Vec { fn from_iter>(iter: I) -> Self { let iter = iter.into_iter(); let (lower, _) = iter.size_hint(); let vec = Vec::with_capacity(lower); for value in iter { vec.push(value); } vec } } impl Extend for Vec { fn extend>(&mut self, iter: I) { let iter = iter.into_iter(); let (lower, _) = iter.size_hint(); self.reserve(lower); for value in iter { self.push(value); } } } impl Clone for Vec { fn clone(&self) -> Vec { self.iter().map(|(_, x)| x).cloned().collect() } } impl fmt::Debug for Vec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter().map(|(_, v)| v)).finish() } } impl PartialEq for Vec { fn eq(&self, other: &Self) -> bool { if self.count() != other.count() { return false; } // Ensure indexes are checked along with values to handle gaps in the vector. for (index, value) in self.iter() { if other.get(index) != Some(value) { return false; } } true } } impl PartialEq for Vec where A: AsRef<[T]>, T: PartialEq, { fn eq(&self, other: &A) -> bool { let other = other.as_ref(); if self.count() != other.len() { return false; } // Ensure indexes are checked along with values to handle gaps in the vector. for (index, value) in self.iter() { if other.get(index) != Some(value) { return false; } } true } } impl Eq for Vec {} boxcar-0.2.7/src/raw.rs000064400000000000000000000417441046102023000130500ustar 00000000000000#![allow(clippy::declare_interior_mutable_const)] use core::cell::UnsafeCell; use core::mem::{self, MaybeUninit}; use core::ops::Index; use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, AtomicUsize, Ordering}; use core::{ptr, slice}; use alloc::boxed::Box; const BUCKETS: usize = (usize::BITS as usize) - SKIP_BUCKET; const MAX_ENTRIES: usize = usize::MAX - SKIP; // A lock-free, append-only vector. pub struct Vec { // A counter used to retrieve a unique index to push to. // // This value may be more than the true length as it will // be incremented before values are actually stored. inflight: AtomicU64, // Buckets of length 32, 64 .. 2^63. buckets: [Bucket; BUCKETS], // The number of initialized elements in this vector. count: AtomicUsize, } unsafe impl Send for Vec {} unsafe impl Sync for Vec {} impl Vec { pub const EMPTY: Vec = Vec { inflight: AtomicU64::new(0), buckets: [Bucket::EMPTY; BUCKETS], count: AtomicUsize::new(0), }; /// Constructs a new, empty `Vec` with the specified capacity. pub fn with_capacity(capacity: usize) -> Vec { let init = match capacity { 0 => 0, // Initialize enough buckets for `capacity` elements. n => Location::of(n).bucket, }; let mut buckets = [Bucket::EMPTY; BUCKETS]; for (i, bucket) in buckets[..=init].iter_mut().enumerate() { let len = Location::bucket_capacity(i); *bucket = Bucket::from_ptr(Bucket::alloc(len)); } Vec { buckets, inflight: AtomicU64::new(0), count: AtomicUsize::new(0), } } pub fn clear(&mut self) { let mut iter = self.iter(); // Consume and reset every entry in the vector. while iter.next_owned(self).is_some() {} // Reset the count. self.count.store(0, Ordering::Relaxed); self.inflight.store(0, Ordering::Relaxed); } /// Returns the number of elements in the vector. pub fn count(&self) -> usize { self.count.load(Ordering::Acquire) } /// Returns a reference to the element at the given index. pub fn get(&self, index: usize) -> Option<&T> { let location = Location::of(index); // Safety: `location.bucket` is always in bounds. let entries = unsafe { self.buckets .get_unchecked(location.bucket) .entries .load(Ordering::Acquire) }; // The bucket is uninitialized. if entries.is_null() { return None; } // Safety: `location.entry` is always in bounds for it's bucket. let entry = unsafe { &*entries.add(location.entry) }; if entry.active.load(Ordering::Acquire) { // Safety: The entry is active. unsafe { return Some(entry.value_unchecked()) } } // The entry is uninitialized. None } /// Returns a reference to the element at the given index. /// /// # Safety /// /// Entry at `index` must be initialized. pub unsafe fn get_unchecked(&self, index: usize) -> &T { let location = Location::of(index); // Safety: Caller guarantees the entry is initialized. unsafe { let entry = self .buckets .get_unchecked(location.bucket) .entries .load(Ordering::Acquire) .add(location.entry); (*entry).value_unchecked() } } /// Returns a mutable reference to the element at the given index. pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { let location = Location::of(index); // Safety: `location.bucket` is always in bounds. let entries = unsafe { self.buckets .get_unchecked_mut(location.bucket) .entries .get_mut() }; // The bucket is uninitialized. if entries.is_null() { return None; } // Safety: `location.entry` is always in bounds for it's bucket. let entry = unsafe { &mut *entries.add(location.entry) }; if *entry.active.get_mut() { // safety: the entry is active unsafe { return Some(entry.value_unchecked_mut()) } } // The entry is uninitialized. None } /// Returns a mutable reference to the element at the given index. /// /// # Safety /// /// Entry at `index` must be initialized. pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { let location = Location::of(index); // Safety: caller guarantees the entry is initialized. unsafe { let entry = self .buckets .get_unchecked_mut(location.bucket) .entries .get_mut() .add(location.entry); (*entry).value_unchecked_mut() } } /// Returns a unique index for insertion. fn next_index(&self) -> usize { let index = self.inflight.fetch_add(1, Ordering::Relaxed); // The inflight counter is a `u64` to catch overflows of the vector's capacity. index.try_into().expect("overflowed maximum capacity") } /// Appends an element returned from the closure to the back of the vector /// at the index represented by the `usize` passed to closure. /// /// This allows for use of the would-be index to be utilized within the /// element. pub fn push_with(&self, f: F) -> usize where F: FnOnce(usize) -> T, { let index = self.next_index(); let value = f(index); self.write(index, value) } /// Appends an element to the back of the vector. pub fn push(&self, value: T) -> usize { self.write(self.next_index(), value) } /// Write an element at the given index. fn write(&self, index: usize, value: T) -> usize { let location = Location::of(index); // Eagerly allocate the next bucket if we are close to the end of this one. if index == (location.bucket_len - (location.bucket_len >> 3)) { if let Some(next_bucket) = self.buckets.get(location.bucket + 1) { Vec::get_or_alloc(next_bucket, location.bucket_len << 1); } } // Safety: `location.bucket` is always in bounds. let bucket = unsafe { self.buckets.get_unchecked(location.bucket) }; let mut entries = bucket.entries.load(Ordering::Acquire); // The bucket has not been allocated yet. if entries.is_null() { entries = Vec::get_or_alloc(bucket, location.bucket_len); } unsafe { // Safety: `location.entry` is always in bounds for it's bucket. let entry = &*entries.add(location.entry); // Safety: We have unique access to this entry. // // 1. It is impossible for another thread to attempt a `push` // to this location as we retrieved it from `next_index`. // // 2. Any thread trying to `get` this entry will see `!active` // and will not try to access it. entry.slot.get().write(MaybeUninit::new(value)); // Let other threads know that this entry is active. entry.active.store(true, Ordering::Release); } // Increase the element count. self.count.fetch_add(1, Ordering::Release); index } // Race to intialize a bucket. // // Note that we avoid contention on bucket allocation by having a specified // writer eagerly allocate the next bucket. fn get_or_alloc(bucket: &Bucket, len: usize) -> *mut Entry { let entries = Bucket::alloc(len); match bucket.entries.compare_exchange( ptr::null_mut(), entries, Ordering::Release, Ordering::Acquire, ) { Ok(_) => entries, Err(found) => unsafe { Bucket::dealloc(entries, len); found }, } } // Reserves capacity for at least `additional` more elements to be inserted in // the vector. The collection may reserve more space to avoid frequent reallocations. pub fn reserve(&self, additional: usize) { let len = self.count.load(Ordering::Acquire); let mut location = Location::of(len.checked_add(additional).unwrap_or(MAX_ENTRIES)); // Allocate buckets starting from the bucket at `len + additional` and // working our way backwards. loop { // Safety: `location.bucket` is always in bounds. let bucket = unsafe { self.buckets.get_unchecked(location.bucket) }; // Reached an initalized bucket, we're done. if !bucket.entries.load(Ordering::Relaxed).is_null() { break; } // Allocate the bucket. Vec::get_or_alloc(bucket, location.bucket_len); // Reached the first bucket, we're done. if location.bucket == 0 { break; } location.bucket -= 1; location.bucket_len = Location::bucket_capacity(location.bucket); } } // Returns an iterator over the vector. pub fn iter(&self) -> Iter { Iter { index: 0, yielded: 0, location: Location { bucket: 0, entry: 0, bucket_len: Location::bucket_capacity(0), }, } } } impl Index for Vec { type Output = T; fn index(&self, index: usize) -> &Self::Output { self.get(index).expect("no element found at index {index}") } } impl Drop for Vec { fn drop(&mut self) { for (i, bucket) in self.buckets.iter_mut().enumerate() { let entries = *bucket.entries.get_mut(); if entries.is_null() { break; } let len = Location::bucket_capacity(i); // Safety: We have `&mut self`. unsafe { Bucket::dealloc(entries, len) } } } } /// A bucket of entries. struct Bucket { entries: AtomicPtr>, } impl Bucket { const EMPTY: Bucket = Bucket::from_ptr(ptr::null_mut()); } /// A possibly uninitialized entry in the vector. struct Entry { slot: UnsafeCell>, active: AtomicBool, } impl Bucket { /// Cast a pointer to an `Entry` to a bucket pointer. const fn from_ptr(entries: *mut Entry) -> Bucket { Bucket { entries: AtomicPtr::new(entries), } } /// Allocate a bucket of the specified capacity. fn alloc(len: usize) -> *mut Entry { let entries = (0..len) .map(|_| Entry:: { slot: UnsafeCell::new(MaybeUninit::uninit()), active: AtomicBool::new(false), }) .collect::]>>(); Box::into_raw(entries) as _ } /// Deallocate a bucket of the specified capacity. unsafe fn dealloc(entries: *mut Entry, len: usize) { unsafe { drop(Box::from_raw(slice::from_raw_parts_mut(entries, len))) } } } impl Entry { // # Safety // // The value must be initialized. unsafe fn value_unchecked(&self) -> &T { // Safety: Guaranteed by caller. unsafe { (*self.slot.get()).assume_init_ref() } } // # Safety // // The value must be initialized. unsafe fn value_unchecked_mut(&mut self) -> &mut T { // Safety: Guaranteed by caller. unsafe { self.slot.get_mut().assume_init_mut() } } } impl Drop for Entry { fn drop(&mut self) { if *self.active.get_mut() { unsafe { ptr::drop_in_place((*self.slot.get()).as_mut_ptr()) } } } } #[derive(Debug, Clone)] struct Location { // The index of the bucket. bucket: usize, // The length of `bucket`. bucket_len: usize, // The index of the entry in the bucket. entry: usize, } // Skip shorter buckets to avoid unnecessary allocations. const SKIP: usize = 32; const SKIP_BUCKET: usize = ((usize::BITS - SKIP.leading_zeros()) as usize) - 1; impl Location { /// Returns the location of a given entry in a vector. fn of(index: usize) -> Location { let skipped = index.checked_add(SKIP).expect("exceeded maximum length"); let bucket = usize::BITS - skipped.leading_zeros(); let bucket = (bucket as usize) - (SKIP_BUCKET + 1); let bucket_len = Location::bucket_capacity(bucket); let entry = skipped ^ bucket_len; Location { bucket, bucket_len, entry, } } /// Returns the capacity of the bucket at the given index. fn bucket_capacity(bucket: usize) -> usize { 1 << (bucket + SKIP_BUCKET) } } /// An iterator over the elements of a [`Vec`]. #[derive(Clone)] pub struct Iter { location: Location, yielded: usize, index: usize, } impl Iter { /// Returns a pointer to the next entry in the iterator. fn next(&mut self, vec: &Vec) -> Option<(usize, *mut Entry)> { // We returned every entry in the vector, we're done. if self.yielded == vec.count() { return None; } // It is possible that the the length was incremented due to an element // being stored in a bucket that we have already iterated over, so we // still have to check that we are in bounds. while self.location.bucket < BUCKETS { // Safety: Bounds checked above. let entries = unsafe { vec.buckets .get_unchecked(self.location.bucket) .entries .load(Ordering::Acquire) }; // Despite this bucket not being initialized, it is possible, but rare, // that a subsequent bucket was initialized before this one. Thus we // have to continue checking every bucket until we yield `vec.count()` // elements. if !entries.is_null() { while self.location.entry < self.location.bucket_len { // Safety: Bounds checked above. let entry = unsafe { entries.add(self.location.entry) }; let index = self.index; self.location.entry += 1; self.index += 1; // Continue even after we find an uninitialized entry for the same // reason as uninitialized buckets. if unsafe { (*entry).active.load(Ordering::Acquire) } { self.yielded += 1; return Some((index, entry)); } } } self.location.entry = 0; self.location.bucket += 1; if self.location.bucket < BUCKETS { self.location.bucket_len = Location::bucket_capacity(self.location.bucket); } } None } /// Returns a shared reference to the next entry in the iterator. pub fn next_shared<'v, T>(&mut self, vec: &'v Vec) -> Option<(usize, &'v T)> { self.next(vec) .map(|(index, entry)| (index, unsafe { (*entry).value_unchecked() })) } /// Returns an owned reference to the next entry in the iterator, resetting the entry in /// the vector. pub fn next_owned(&mut self, vec: &mut Vec) -> Option { self.next(vec).map(|(_, entry)| { // Safety: We have `&mut Vec`. let entry = unsafe { &mut *entry }; *entry.active.get_mut() = false; // Safety: `Iter::next` only yields initialized entries. unsafe { mem::replace(&mut *entry.slot.get(), MaybeUninit::uninit()).assume_init() } }) } /// Returns the number of elements that have been yielded by this iterator. pub fn yielded(&self) -> usize { self.yielded } } #[cfg(test)] mod tests { use super::*; #[test] fn location() { assert_eq!(Location::bucket_capacity(0), 32); for i in 0..32 { let loc = Location::of(i); assert_eq!(loc.bucket_len, 32); assert_eq!(loc.bucket, 0); assert_eq!(loc.entry, i); } assert_eq!(Location::bucket_capacity(1), 64); for i in 33..96 { let loc = Location::of(i); assert_eq!(loc.bucket_len, 64); assert_eq!(loc.bucket, 1); assert_eq!(loc.entry, i - 32); } assert_eq!(Location::bucket_capacity(2), 128); for i in 96..224 { let loc = Location::of(i); assert_eq!(loc.bucket_len, 128); assert_eq!(loc.bucket, 2); assert_eq!(loc.entry, i - 96); } let max = Location::of(MAX_ENTRIES); assert_eq!(max.bucket, BUCKETS - 1); assert_eq!(max.bucket_len, 1 << 63); assert_eq!(max.entry, (1 << 63) - 1); } } boxcar-0.2.7/tests/vec.rs000064400000000000000000000047421046102023000134040ustar 00000000000000use std::sync::atomic::{AtomicUsize, Ordering}; use std::{sync::Barrier, thread}; #[test] fn simple() { let vec = boxcar::vec![0, 1, 2]; for x in 3..1000 { let i = vec.push(x); assert_eq!(vec[i], x); } for i in 0..1000 { assert_eq!(vec[i], i); } for (i, &x) in vec.iter() { assert_eq!(i, x); } for (i, x) in vec.into_iter().enumerate() { assert_eq!(i, x); } } #[test] fn clear() { struct T<'a>(&'a AtomicUsize); impl Drop for T<'_> { fn drop(&mut self) { self.0.fetch_add(1, Ordering::Relaxed); } } let drops = AtomicUsize::new(0); let mut vec = boxcar::Vec::new(); vec.push(T(&drops)); vec.push(T(&drops)); let first_ptr: *const _ = vec.iter().next().unwrap().1 as _; vec.clear(); assert_eq!(vec.count(), 0); assert_eq!(vec.iter().count(), 0); assert_eq!(drops.swap(0, Ordering::Relaxed), 2); vec.clear(); assert_eq!(vec.count(), 0); assert_eq!(vec.iter().count(), 0); assert_eq!(drops.load(Ordering::Relaxed), 0); vec.push(T(&drops)); let ptr: *const _ = vec.iter().next().unwrap().1 as _; assert_eq!(ptr, first_ptr); drop(vec); assert_eq!(drops.load(Ordering::Relaxed), 1); } #[test] fn stress() { let vec = boxcar::Vec::new(); let barrier = Barrier::new(6); thread::scope(|s| { s.spawn(|| { barrier.wait(); for i in 0..1000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 1000..2000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 2000..3000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 3000..4000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 0..10_000 { if let Some(&x) = vec.get(i) { assert!(x < 4000); } } }); s.spawn(|| { barrier.wait(); for (i, &x) in vec.iter() { assert!(x < 4000); assert!(vec[i] < 4000); } }); }); assert_eq!(vec.count(), 4000); let mut sorted = vec.into_iter().collect::>(); sorted.sort(); assert_eq!(sorted, (0..4000).collect::>()); }