├── .github ├── DOCS.md └── workflows │ ├── check.yml │ ├── safety.yml │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── bench.rs ├── src ├── lib.rs ├── loom.rs └── raw.rs └── tests ├── loom.rs └── vec.rs /.github/DOCS.md: -------------------------------------------------------------------------------- 1 | Workflows adapted from https://github.com/jonhoo/rust-ci-conf. 2 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs 2 | # several checks: 3 | # - fmt: checks that the code is formatted according to rustfmt 4 | # - clippy: checks that the code does not contain any clippy warnings 5 | # - doc: checks that the code can be documented without errors 6 | # - hack: check combinations of feature flags 7 | # - msrv: check that the msrv specified in the crate is correct 8 | permissions: 9 | contents: read 10 | # This configuration allows maintainers of this repo to create a branch and pull request based on 11 | # the new branch. Restricting the push trigger to the main branch ensures that the PR only gets 12 | # built once. 13 | on: 14 | push: 15 | branches: [master] 16 | pull_request: 17 | # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that 18 | # we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 21 | cancel-in-progress: true 22 | name: check 23 | jobs: 24 | fmt: 25 | runs-on: ubuntu-latest 26 | name: stable / fmt 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - name: Install stable 32 | uses: dtolnay/rust-toolchain@stable 33 | with: 34 | components: rustfmt 35 | - name: cargo fmt --check 36 | run: cargo fmt --check 37 | clippy: 38 | runs-on: ubuntu-latest 39 | name: ${{ matrix.toolchain }} / clippy 40 | permissions: 41 | contents: read 42 | checks: write 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | # Get early warning of new lints which are regularly introduced in beta channels. 47 | toolchain: [stable, beta] 48 | steps: 49 | - uses: actions/checkout@v4 50 | with: 51 | submodules: true 52 | - name: Install ${{ matrix.toolchain }} 53 | uses: dtolnay/rust-toolchain@master 54 | with: 55 | toolchain: ${{ matrix.toolchain }} 56 | components: clippy 57 | - name: cargo clippy 58 | uses: giraffate/clippy-action@v1 59 | with: 60 | reporter: 'github-pr-check' 61 | github_token: ${{ secrets.GITHUB_TOKEN }} 62 | semver: 63 | runs-on: ubuntu-latest 64 | name: semver 65 | steps: 66 | - uses: actions/checkout@v4 67 | with: 68 | submodules: true 69 | - name: Install stable 70 | uses: dtolnay/rust-toolchain@stable 71 | with: 72 | components: rustfmt 73 | - name: cargo-semver-checks 74 | uses: obi1kenobi/cargo-semver-checks-action@v2 75 | doc: 76 | # run docs generation on nightly rather than stable. This enables features like 77 | # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an 78 | # API be documented as only available in some specific platforms. 79 | runs-on: ubuntu-latest 80 | name: nightly / doc 81 | steps: 82 | - uses: actions/checkout@v4 83 | with: 84 | submodules: true 85 | - name: Install nightly 86 | uses: dtolnay/rust-toolchain@nightly 87 | - name: cargo doc 88 | run: cargo doc --no-deps --all-features 89 | env: 90 | RUSTDOCFLAGS: --cfg docsrs 91 | hack: 92 | # cargo-hack checks combinations of feature flags to ensure that features are all additive 93 | # which is required for feature unification 94 | runs-on: ubuntu-latest 95 | name: ubuntu / stable / features 96 | steps: 97 | - uses: actions/checkout@v4 98 | with: 99 | submodules: true 100 | - name: Install stable 101 | uses: dtolnay/rust-toolchain@stable 102 | - name: cargo install cargo-hack 103 | uses: taiki-e/install-action@cargo-hack 104 | # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4 105 | # --feature-powerset runs for every combination of features 106 | - name: cargo hack 107 | run: cargo hack --feature-powerset check 108 | msrv: 109 | # check that we can build using the minimal rust version that is specified by this crate 110 | runs-on: ubuntu-latest 111 | # we use a matrix here just because env can't be used in job names 112 | # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability 113 | strategy: 114 | matrix: 115 | msrv: ["1.72.0"] 116 | name: ubuntu / ${{ matrix.msrv }} 117 | steps: 118 | - uses: actions/checkout@v4 119 | with: 120 | submodules: true 121 | - name: Install ${{ matrix.msrv }} 122 | uses: dtolnay/rust-toolchain@master 123 | with: 124 | toolchain: ${{ matrix.msrv }} 125 | - name: cargo +${{ matrix.msrv }} check 126 | run: cargo check 127 | -------------------------------------------------------------------------------- /.github/workflows/safety.yml: -------------------------------------------------------------------------------- 1 | # Runs: 2 | # - miri - detects undefined behavior and memory leaks 3 | # - address sanitizer - detects memory errors 4 | # - leak sanitizer - detects memory leaks 5 | # - loom - detects weak memory ordering bugs 6 | # See check.yml for information about how the concurrency cancellation and workflow triggering works 7 | permissions: 8 | contents: read 9 | on: 10 | push: 11 | branches: [master] 12 | pull_request: 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | name: safety 17 | jobs: 18 | sanitizers: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 15 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - name: Install nightly 26 | uses: dtolnay/rust-toolchain@nightly 27 | - run: | 28 | # to get the symbolizer for debug symbol resolution 29 | sudo apt install llvm 30 | # to fix buggy leak analyzer: 31 | # https://github.com/japaric/rust-san#unrealiable-leaksanitizer 32 | # ensure there's a profile.dev section 33 | if ! grep -qE '^[ \t]*[profile.dev]' Cargo.toml; then 34 | echo >> Cargo.toml 35 | echo '[profile.dev]' >> Cargo.toml 36 | fi 37 | # remove pre-existing opt-levels in profile.dev 38 | sed -i '/^\s*\[profile.dev\]/,/^\s*\[/ {/^\s*opt-level/d}' Cargo.toml 39 | # now set opt-level to 1 40 | sed -i '/^\s*\[profile.dev\]/a opt-level = 1' Cargo.toml 41 | cat Cargo.toml 42 | name: Enable debug symbols 43 | - name: cargo test -Zsanitizer=address 44 | # only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945 45 | run: cargo test --lib --tests --all-features --target x86_64-unknown-linux-gnu 46 | env: 47 | ASAN_OPTIONS: "detect_odr_violation=0:detect_leaks=0" 48 | RUSTFLAGS: "-Z sanitizer=address" 49 | - name: cargo test -Zsanitizer=leak 50 | if: always() 51 | run: cargo test --all-features --target x86_64-unknown-linux-gnu 52 | env: 53 | LSAN_OPTIONS: "suppressions=lsan-suppressions.txt" 54 | RUSTFLAGS: "-Z sanitizer=leak" 55 | miri: 56 | runs-on: ubuntu-latest 57 | timeout-minutes: 15 58 | steps: 59 | - uses: actions/checkout@v4 60 | with: 61 | submodules: true 62 | - run: | 63 | echo "NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)" >> $GITHUB_ENV 64 | - name: Install ${{ env.NIGHTLY }} 65 | uses: dtolnay/rust-toolchain@master 66 | with: 67 | toolchain: ${{ env.NIGHTLY }} 68 | components: miri 69 | - name: cargo miri test 70 | run: cargo miri test 71 | loom: 72 | runs-on: ubuntu-latest 73 | timeout-minutes: 15 74 | steps: 75 | - uses: actions/checkout@v4 76 | with: 77 | submodules: true 78 | - name: Install stable 79 | uses: dtolnay/rust-toolchain@master 80 | with: 81 | toolchain: stable 82 | - run: cargo test --test loom --features loom --release 83 | env: 84 | RUSTFLAGS: --cfg loom 85 | 86 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This is the main CI workflow that runs the test suite on all pushes to main and all pull requests. 2 | # It runs the following jobs: 3 | # - required: runs the test suite on ubuntu with stable and beta rust toolchains 4 | # requirements of this crate, and its dependencies 5 | # - os-check: runs the test suite on mac and windows 6 | # See check.yml for information about how the concurrency cancellation and workflow triggering works 7 | permissions: 8 | contents: read 9 | on: 10 | push: 11 | branches: [master] 12 | pull_request: 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | name: test 17 | jobs: 18 | required: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 15 21 | name: ubuntu / ${{ matrix.toolchain }} 22 | strategy: 23 | matrix: 24 | # run on stable and beta to ensure that tests won't break on the next version of the rust 25 | # toolchain 26 | toolchain: [stable, beta] 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - name: Install ${{ matrix.toolchain }} 32 | uses: dtolnay/rust-toolchain@master 33 | with: 34 | toolchain: ${{ matrix.toolchain }} 35 | - name: cargo generate-lockfile 36 | # enable this ci template to run regardless of whether the lockfile is checked in or not 37 | if: hashFiles('Cargo.lock') == '' 38 | run: cargo generate-lockfile 39 | # https://twitter.com/jonhoo/status/1571290371124260865 40 | - name: cargo test --locked 41 | run: cargo test --locked --all-features --all-targets 42 | # https://github.com/rust-lang/cargo/issues/6669 43 | - name: cargo test --doc 44 | run: cargo test --locked --all-features --doc 45 | os-check: 46 | # run cargo test on mac and windows 47 | runs-on: ${{ matrix.os }} 48 | timeout-minutes: 15 49 | name: ${{ matrix.os }} / stable 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | os: [macos-latest, windows-latest] 54 | steps: 55 | - uses: actions/checkout@v4 56 | with: 57 | submodules: true 58 | - name: Install stable 59 | uses: dtolnay/rust-toolchain@stable 60 | - name: cargo generate-lockfile 61 | if: hashFiles('Cargo.lock') == '' 62 | run: cargo generate-lockfile 63 | - name: cargo test 64 | run: cargo test --locked --all-features --all-targets 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boxcar" 3 | version = "0.2.11" 4 | authors = ["Ibraheem Ahmed "] 5 | edition = "2021" 6 | license = "MIT" 7 | rust-version = "1.72.0" 8 | 9 | readme = "README.md" 10 | description = "A concurrent, append-only vector" 11 | repository = "https://github.com/ibraheemdev/boxcar" 12 | 13 | categories = ["concurrency", "data-structures"] 14 | keywords = ["concurrent", "vector", "atomic", "lock-free"] 15 | exclude = [ 16 | ".gitignore", 17 | ".github/**", 18 | ] 19 | 20 | [[bench]] 21 | name = "bench" 22 | harness = false 23 | 24 | [dependencies] 25 | 26 | [target.'cfg(loom)'.dependencies] 27 | loom = { version = "0.7", optional = true } 28 | 29 | [lints.rust] 30 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] } 31 | 32 | [dev-dependencies] 33 | criterion = "0.5" 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `boxcar` 2 | 3 | [crates.io](https://crates.io/crates/boxcar) 4 | [github](https://github.com/ibraheemdev/boxcar) 5 | [docs.rs](https://docs.rs/boxcar) 6 | 7 | A concurrent, append-only vector. 8 | 9 | The vector provided by this crate supports lock-free `get` and `push` operations. 10 | The vector grows internally but never reallocates, so element addresses are stable 11 | for the lifetime of the vector. Additionally, both `get` and `push` run in constant-time. 12 | 13 | ## Examples 14 | 15 | Appending an element to a vector and retrieving it: 16 | 17 | ```rust 18 | let vec = boxcar::Vec::new(); 19 | let i = vec.push(42); 20 | assert_eq!(vec[i], 42); 21 | ``` 22 | 23 | The vector can be modified by multiple threads concurrently: 24 | 25 | ```rust 26 | let vec = boxcar::Vec::new(); 27 | 28 | // Spawn a few threads that append to the vector. 29 | std::thread::scope(|s| for i in 0..6 { 30 | let vec = &vec; 31 | 32 | s.spawn(move || { 33 | // Push through the shared reference. 34 | vec.push(i); 35 | }); 36 | }); 37 | 38 | for i in 0..6 { 39 | assert!(vec.iter().any(|(_, &x)| x == i)); 40 | } 41 | ``` 42 | 43 | Elements can be mutated through fine-grained locking: 44 | 45 | ```rust 46 | let vec = boxcar::Vec::new(); 47 | 48 | std::thread::scope(|s| { 49 | // Insert an element. 50 | vec.push(std::sync::Mutex::new(0)); 51 | 52 | s.spawn(|| { 53 | // Mutate through the lock. 54 | *vec[0].lock().unwrap() += 1; 55 | }); 56 | }); 57 | 58 | let x = vec[0].lock().unwrap(); 59 | assert_eq!(*x, 1); 60 | ``` 61 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | // adapted from: https://github.com/hawkw/sharded-slab/blob/main/benches/bench.rs 2 | // which carries the following MIT license: 3 | // 4 | // Copyright (c) 2019 Eliza Weisman 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 24 | use std::{ 25 | sync::{Arc, Barrier, RwLock}, 26 | thread, 27 | time::{Duration, Instant}, 28 | }; 29 | 30 | #[derive(Clone)] 31 | struct Harness { 32 | start: Arc, 33 | end: Arc, 34 | vec: Arc, 35 | } 36 | 37 | const THREADS: usize = 12; 38 | 39 | impl Harness 40 | where 41 | T: Send + Sync + 'static, 42 | { 43 | fn new(vec: Arc) -> Self { 44 | Self { 45 | start: Arc::new(Barrier::new(THREADS + 1)), 46 | end: Arc::new(Barrier::new(THREADS + 1)), 47 | vec, 48 | } 49 | } 50 | 51 | fn run(&self, f: impl FnOnce(&T) + Send + Copy + 'static) -> Duration { 52 | for _ in 0..THREADS { 53 | let start = self.start.clone(); 54 | let end = self.end.clone(); 55 | let vec = self.vec.clone(); 56 | thread::spawn(move || { 57 | start.wait(); 58 | f(&*vec); 59 | end.wait(); 60 | }); 61 | } 62 | 63 | self.start.wait(); 64 | let t0 = Instant::now(); 65 | self.end.wait(); 66 | t0.elapsed() 67 | } 68 | } 69 | 70 | fn write_read(c: &mut Criterion) { 71 | const WORKLOAD: &[usize] = &[100, 1000, 10_000, 50_000, 100_000]; 72 | 73 | let mut group = c.benchmark_group("push_get"); 74 | group.measurement_time(Duration::from_secs(15)); 75 | 76 | for i in WORKLOAD { 77 | group.bench_with_input(BenchmarkId::new("boxcar::Vec<_>", i), i, |b, &i| { 78 | b.iter_custom(|iters| { 79 | let mut total = Duration::from_secs(0); 80 | for _ in 0..iters { 81 | let bench = Harness::new(Arc::new(boxcar::Vec::new())); 82 | 83 | let elapsed = bench.run(move |vec: &boxcar::Vec| { 84 | let v: Vec<_> = (0..i).map(|_| vec.push(true)).collect(); 85 | for i in v { 86 | assert!(vec.get(i).unwrap()); 87 | } 88 | }); 89 | 90 | total += elapsed; 91 | } 92 | total 93 | }) 94 | }); 95 | 96 | group.bench_with_input(BenchmarkId::new("RwLock>", i), i, |b, &i| { 97 | b.iter_custom(|iters| { 98 | let mut total = Duration::from_secs(0); 99 | for _ in 0..iters { 100 | let bench = Harness::new(Arc::new(RwLock::new(Vec::new()))); 101 | 102 | let elapsed = bench.run(move |vec: &RwLock>| { 103 | let v: Vec<_> = (0..i) 104 | .map(|_| { 105 | let mut vec = vec.write().unwrap(); 106 | vec.push(true); 107 | vec.len() - 1 108 | }) 109 | .collect(); 110 | for i in v { 111 | assert!(vec.read().unwrap().get(i).unwrap()); 112 | } 113 | }); 114 | 115 | total += elapsed; 116 | } 117 | total 118 | }) 119 | }); 120 | } 121 | 122 | group.finish(); 123 | } 124 | 125 | criterion_group!(benches, write_read); 126 | criterion_main!(benches); 127 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![deny(unsafe_op_in_unsafe_fn)] 3 | #![allow(clippy::needless_doctest_main)] 4 | #![no_std] 5 | 6 | extern crate alloc; 7 | 8 | mod loom; 9 | mod raw; 10 | 11 | use core::fmt; 12 | use core::ops::Index; 13 | 14 | /// Creates a [`Vec`] containing the given elements. 15 | /// 16 | /// `vec!` allows `Vec`s to be defined with the same syntax as array expressions. 17 | /// There are two forms of this macro: 18 | /// 19 | /// - Create a [`Vec`] containing a given list of elements: 20 | /// 21 | /// ``` 22 | /// let vec = vec![1, 2, 3]; 23 | /// assert_eq!(vec[0], 1); 24 | /// assert_eq!(vec[1], 2); 25 | /// assert_eq!(vec[2], 3); 26 | /// ``` 27 | /// 28 | /// - Create a [`Vec`] from a given element and size: 29 | /// 30 | /// ``` 31 | /// let vec = vec![1; 3]; 32 | /// assert_eq!(vec, [1, 1, 1]); 33 | /// ``` 34 | #[macro_export] 35 | macro_rules! vec { 36 | () => { 37 | $crate::Vec::new() 38 | }; 39 | ($elem:expr; $n:expr) => {{ 40 | let n = $n; 41 | let mut vec = $crate::Vec::with_capacity(n); 42 | let iter = ::core::iter::Iterator::take(::core::iter::repeat($elem), n); 43 | ::core::iter::Extend::extend(&mut vec, iter); 44 | vec 45 | }}; 46 | ($($x:expr),+ $(,)?) => ( 47 | <$crate::Vec<_> as ::core::iter::FromIterator<_>>::from_iter([$($x),+]) 48 | ); 49 | } 50 | 51 | /// A concurrent, append-only vector. 52 | /// 53 | /// See [the crate documentation](crate) for details. 54 | /// 55 | /// # Notes 56 | /// 57 | /// The bucket array is stored inline, meaning that the `Vec` type 58 | /// is quite large on the stack. It is expected that you store it behind 59 | /// an [`Arc`](std::sync::Arc) or similar. 60 | pub struct Vec { 61 | raw: raw::Vec, 62 | } 63 | 64 | impl Default for Vec { 65 | fn default() -> Vec { 66 | Vec::new() 67 | } 68 | } 69 | 70 | impl Vec { 71 | /// Constructs a new, empty `Vec`. 72 | /// 73 | /// # Examples 74 | /// 75 | /// ``` 76 | /// let vec: boxcar::Vec = boxcar::Vec::new(); 77 | /// ``` 78 | #[inline] 79 | #[cfg(not(loom))] 80 | pub const fn new() -> Vec { 81 | Vec { 82 | raw: raw::Vec::new(), 83 | } 84 | } 85 | 86 | #[cfg(loom)] 87 | pub fn new() -> Vec { 88 | Vec { 89 | raw: raw::Vec::new(), 90 | } 91 | } 92 | 93 | /// Constructs a new, empty `Vec` with the specified capacity. 94 | /// 95 | /// The vector will be able to hold at least `capacity` elements 96 | /// without reallocating. 97 | /// 98 | /// # Examples 99 | /// 100 | /// ``` 101 | /// let vec = boxcar::Vec::with_capacity(10); 102 | /// 103 | /// for i in 0..10 { 104 | /// // Will not allocate. 105 | /// vec.push(i); 106 | /// } 107 | /// 108 | /// // May allocate. 109 | /// vec.push(11); 110 | /// ``` 111 | #[inline] 112 | pub fn with_capacity(capacity: usize) -> Vec { 113 | Vec { 114 | raw: raw::Vec::with_capacity(capacity), 115 | } 116 | } 117 | 118 | /// Reserves capacity for at least `additional` more elements to be inserted 119 | /// in the given `Vec`. The collection may reserve more space to avoid 120 | /// frequent reallocations. 121 | /// 122 | /// Does nothing if capacity is already sufficient. 123 | /// 124 | /// # Examples 125 | /// 126 | /// ``` 127 | /// let vec = boxcar::Vec::new(); 128 | /// vec.reserve(10); 129 | /// 130 | /// for i in 0..10 { 131 | /// // Will not allocate. 132 | /// vec.push(i); 133 | /// } 134 | /// 135 | /// // May allocate. 136 | /// vec.push(11); 137 | /// ``` 138 | pub fn reserve(&self, additional: usize) { 139 | self.raw.reserve(additional) 140 | } 141 | 142 | /// Appends an element to the back of the vector, 143 | /// returning the index it was inserted into. 144 | /// 145 | /// # Examples 146 | /// 147 | /// ``` 148 | /// let vec = boxcar::vec![1, 2]; 149 | /// assert_eq!(vec.push(3), 2); 150 | /// assert_eq!(vec, [1, 2, 3]); 151 | /// ``` 152 | #[inline] 153 | pub fn push(&self, value: T) -> usize { 154 | self.raw.push(value) 155 | } 156 | 157 | /// Appends the element returned from the closure `f` to the back of the vector 158 | /// at the index supplied to the closure. 159 | /// 160 | /// Returns the index that the element was inserted into. 161 | /// 162 | /// # Examples 163 | /// 164 | /// ``` 165 | /// let vec = boxcar::vec![0, 1]; 166 | /// vec.push_with(|index| index); 167 | /// assert_eq!(vec, [0, 1, 2]); 168 | /// ``` 169 | #[inline] 170 | pub fn push_with(&self, f: F) -> usize 171 | where 172 | F: FnOnce(usize) -> T, 173 | { 174 | self.raw.push_with(f) 175 | } 176 | 177 | /// Returns the number of elements in the vector. 178 | /// 179 | /// Note that due to concurrent writes, it is not guaranteed 180 | /// that all elements `0..vec.count()` are initialized. 181 | /// 182 | /// # Examples 183 | /// 184 | /// ``` 185 | /// let vec = boxcar::Vec::new(); 186 | /// assert_eq!(vec.count(), 0); 187 | /// vec.push(1); 188 | /// vec.push(2); 189 | /// assert_eq!(vec.count(), 2); 190 | /// ``` 191 | #[inline] 192 | pub fn count(&self) -> usize { 193 | self.raw.count() 194 | } 195 | 196 | /// Returns `true` if the vector contains no elements. 197 | /// 198 | /// # Examples 199 | /// 200 | /// ``` 201 | /// let vec = boxcar::Vec::new(); 202 | /// assert!(vec.is_empty()); 203 | /// 204 | /// vec.push(1); 205 | /// assert!(!vec.is_empty()); 206 | /// ``` 207 | #[inline] 208 | pub fn is_empty(&self) -> bool { 209 | self.count() == 0 210 | } 211 | 212 | /// Returns a reference to the element at the given index. 213 | /// 214 | /// # Examples 215 | /// 216 | /// ``` 217 | /// let vec = boxcar::vec![10, 40, 30]; 218 | /// assert_eq!(Some(&40), vec.get(1)); 219 | /// assert_eq!(None, vec.get(3)); 220 | /// ``` 221 | #[inline] 222 | pub fn get(&self, index: usize) -> Option<&T> { 223 | self.raw.get(index) 224 | } 225 | 226 | /// Returns a mutable reference to the element at the given index. 227 | /// 228 | /// # Examples 229 | /// 230 | /// ``` 231 | /// let mut vec = boxcar::vec![10, 40, 30]; 232 | /// assert_eq!(Some(&mut 40), vec.get_mut(1)); 233 | /// assert_eq!(None, vec.get_mut(3)); 234 | /// ``` 235 | #[inline] 236 | pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { 237 | self.raw.get_mut(index) 238 | } 239 | 240 | /// Returns a reference to an element, without doing bounds 241 | /// checking or verifying that the element is fully initialized. 242 | /// 243 | /// For a safe alternative see [`get`](Vec::get). 244 | /// 245 | /// # Safety 246 | /// 247 | /// Calling this method with an out-of-bounds index, or for an element that 248 | /// is being concurrently initialized is **undefined behavior**, even if 249 | /// the resulting reference is not used. 250 | /// 251 | /// # Examples 252 | /// 253 | /// ``` 254 | /// let vec = boxcar::vec![1, 2, 4]; 255 | /// 256 | /// unsafe { 257 | /// assert_eq!(vec.get_unchecked(1), &2); 258 | /// } 259 | /// ``` 260 | #[inline] 261 | pub unsafe fn get_unchecked(&self, index: usize) -> &T { 262 | // Safety: Guaranteed by caller. 263 | unsafe { self.raw.get_unchecked(index) } 264 | } 265 | 266 | /// Returns a mutable reference to an element, without doing bounds 267 | /// checking or verifying that the element is fully initialized. 268 | /// 269 | /// For a safe alternative see [`get`](Vec::get). 270 | /// 271 | /// # Safety 272 | /// 273 | /// Calling this method with an out-of-bounds index is **undefined 274 | /// behavior**, even if the resulting reference is not used. 275 | /// 276 | /// # Examples 277 | /// 278 | /// ``` 279 | /// let mut vec = boxcar::vec![1, 2, 4]; 280 | /// 281 | /// unsafe { 282 | /// assert_eq!(vec.get_unchecked_mut(1), &mut 2); 283 | /// } 284 | /// ``` 285 | #[inline] 286 | pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { 287 | // Safety: Guaranteed by caller. 288 | unsafe { self.raw.get_unchecked_mut(index) } 289 | } 290 | 291 | /// Returns an iterator over the vector. 292 | /// 293 | /// Values are yielded in the form `(index, value)`. The vector may 294 | /// have in-progress concurrent writes that create gaps, so `index` 295 | /// may not be strictly sequential. 296 | /// 297 | /// # Examples 298 | /// 299 | /// ``` 300 | /// let vec = boxcar::vec![1, 2, 4]; 301 | /// let mut iterator = vec.iter(); 302 | /// 303 | /// assert_eq!(iterator.next(), Some((0, &1))); 304 | /// assert_eq!(iterator.next(), Some((1, &2))); 305 | /// assert_eq!(iterator.next(), Some((2, &4))); 306 | /// assert_eq!(iterator.next(), None); 307 | /// ``` 308 | #[inline] 309 | pub fn iter(&self) -> Iter<'_, T> { 310 | Iter { 311 | vec: &self.raw, 312 | raw: self.raw.iter(), 313 | } 314 | } 315 | 316 | /// Clears the vector, removing all values. 317 | /// 318 | /// Note that this method has no effect on the allocated capacity 319 | /// of the vector. 320 | /// 321 | /// # Examples 322 | /// 323 | /// ``` 324 | /// let mut vec = boxcar::Vec::new(); 325 | /// vec.push(1); 326 | /// vec.push(2); 327 | /// 328 | /// vec.clear(); 329 | /// assert!(vec.is_empty()); 330 | /// 331 | /// vec.push(3); // Will not allocate. 332 | /// ``` 333 | #[inline] 334 | pub fn clear(&mut self) { 335 | self.raw.clear(); 336 | } 337 | } 338 | 339 | impl Index for Vec { 340 | type Output = T; 341 | 342 | #[inline] 343 | fn index(&self, index: usize) -> &Self::Output { 344 | &self.raw[index] 345 | } 346 | } 347 | 348 | impl IntoIterator for Vec { 349 | type Item = T; 350 | type IntoIter = IntoIter; 351 | 352 | fn into_iter(self) -> Self::IntoIter { 353 | IntoIter { 354 | raw: self.raw.iter(), 355 | vec: self.raw, 356 | } 357 | } 358 | } 359 | 360 | impl<'a, T> IntoIterator for &'a Vec { 361 | type Item = (usize, &'a T); 362 | type IntoIter = Iter<'a, T>; 363 | 364 | fn into_iter(self) -> Self::IntoIter { 365 | self.iter() 366 | } 367 | } 368 | 369 | /// An iterator that moves out of a vector. 370 | /// 371 | /// This struct is created by the `into_iter` method on [`Vec`] 372 | /// (provided by the [`IntoIterator`] trait). 373 | pub struct IntoIter { 374 | vec: raw::Vec, 375 | raw: raw::Iter, 376 | } 377 | 378 | impl Iterator for IntoIter { 379 | type Item = T; 380 | 381 | #[inline] 382 | fn next(&mut self) -> Option { 383 | self.raw.next_owned(&mut self.vec) 384 | } 385 | 386 | #[inline] 387 | fn size_hint(&self) -> (usize, Option) { 388 | let remaining = self.vec.count() - self.raw.yielded(); 389 | (remaining, Some(remaining)) 390 | } 391 | } 392 | 393 | /// An iterator over the elements of a [`Vec`]. 394 | /// 395 | /// See [`Vec::iter`] for details. 396 | pub struct Iter<'a, T> { 397 | vec: &'a raw::Vec, 398 | raw: raw::Iter, 399 | } 400 | 401 | impl<'a, T> Clone for Iter<'a, T> { 402 | fn clone(&self) -> Iter<'a, T> { 403 | Iter { 404 | vec: self.vec, 405 | raw: self.raw.clone(), 406 | } 407 | } 408 | } 409 | 410 | impl<'a, T> Iterator for Iter<'a, T> { 411 | type Item = (usize, &'a T); 412 | 413 | #[inline] 414 | fn next(&mut self) -> Option { 415 | self.raw.next_shared(self.vec) 416 | } 417 | 418 | #[inline] 419 | fn size_hint(&self) -> (usize, Option) { 420 | (self.vec.count() - self.raw.yielded(), None) 421 | } 422 | } 423 | 424 | impl fmt::Debug for Iter<'_, T> { 425 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 426 | struct Contents<'a, T>(&'a T); 427 | 428 | impl fmt::Debug for Contents<'_, T> 429 | where 430 | T: Iterator + Clone, 431 | T::Item: fmt::Debug, 432 | { 433 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 434 | f.debug_list().entries(self.0.clone()).finish() 435 | } 436 | } 437 | 438 | f.debug_tuple("Iter").field(&Contents(self)).finish() 439 | } 440 | } 441 | 442 | impl FromIterator for Vec { 443 | fn from_iter>(iter: I) -> Self { 444 | let iter = iter.into_iter(); 445 | 446 | let (lower, _) = iter.size_hint(); 447 | let vec = Vec::with_capacity(lower); 448 | 449 | for value in iter { 450 | vec.push(value); 451 | } 452 | 453 | vec 454 | } 455 | } 456 | 457 | impl Extend for Vec { 458 | #[inline] 459 | fn extend>(&mut self, iter: I) { 460 | let iter = iter.into_iter(); 461 | 462 | let (lower, _) = iter.size_hint(); 463 | self.reserve(lower); 464 | 465 | for value in iter { 466 | self.push(value); 467 | } 468 | } 469 | } 470 | 471 | impl Clone for Vec { 472 | fn clone(&self) -> Vec { 473 | self.iter().map(|(_, x)| x).cloned().collect() 474 | } 475 | } 476 | 477 | impl fmt::Debug for Vec { 478 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 479 | f.debug_list().entries(self.iter().map(|(_, v)| v)).finish() 480 | } 481 | } 482 | 483 | impl PartialEq for Vec { 484 | fn eq(&self, other: &Self) -> bool { 485 | if self.count() != other.count() { 486 | return false; 487 | } 488 | 489 | // Ensure indexes are checked along with values to handle gaps in the vector. 490 | for (index, value) in self.iter() { 491 | if other.get(index) != Some(value) { 492 | return false; 493 | } 494 | } 495 | 496 | true 497 | } 498 | } 499 | 500 | impl PartialEq for Vec 501 | where 502 | A: AsRef<[T]>, 503 | T: PartialEq, 504 | { 505 | fn eq(&self, other: &A) -> bool { 506 | let other = other.as_ref(); 507 | 508 | if self.count() != other.len() { 509 | return false; 510 | } 511 | 512 | // Ensure indexes are checked along with values to handle gaps in the vector. 513 | for (index, value) in self.iter() { 514 | if other.get(index) != Some(value) { 515 | return false; 516 | } 517 | } 518 | 519 | true 520 | } 521 | } 522 | 523 | impl Eq for Vec {} 524 | -------------------------------------------------------------------------------- /src/loom.rs: -------------------------------------------------------------------------------- 1 | pub use self::inner::*; 2 | 3 | pub trait AtomicMut { 4 | fn read_mut(&mut self) -> T; 5 | fn write_mut(&mut self, value: T); 6 | } 7 | 8 | #[cfg(loom)] 9 | mod inner { 10 | pub use loom::{cell, sync::atomic}; 11 | 12 | impl super::AtomicMut for atomic::AtomicBool { 13 | fn read_mut(&mut self) -> bool { 14 | self.load(atomic::Ordering::Relaxed) 15 | } 16 | 17 | fn write_mut(&mut self, value: bool) { 18 | self.store(value, atomic::Ordering::Relaxed) 19 | } 20 | } 21 | 22 | impl super::AtomicMut<*mut T> for atomic::AtomicPtr { 23 | fn read_mut(&mut self) -> *mut T { 24 | self.load(atomic::Ordering::Relaxed) 25 | } 26 | 27 | fn write_mut(&mut self, value: *mut T) { 28 | self.store(value, atomic::Ordering::Relaxed) 29 | } 30 | } 31 | } 32 | 33 | #[cfg(not(loom))] 34 | mod inner { 35 | pub use core::sync::atomic; 36 | 37 | impl super::AtomicMut for atomic::AtomicBool { 38 | #[inline(always)] 39 | fn read_mut(&mut self) -> bool { 40 | *self.get_mut() 41 | } 42 | 43 | #[inline(always)] 44 | fn write_mut(&mut self, value: bool) { 45 | *self.get_mut() = value; 46 | } 47 | } 48 | 49 | impl super::AtomicMut<*mut T> for atomic::AtomicPtr { 50 | #[inline(always)] 51 | fn read_mut(&mut self) -> *mut T { 52 | *self.get_mut() 53 | } 54 | 55 | #[inline(always)] 56 | fn write_mut(&mut self, value: *mut T) { 57 | *self.get_mut() = value; 58 | } 59 | } 60 | 61 | pub mod cell { 62 | pub struct UnsafeCell(core::cell::UnsafeCell); 63 | 64 | impl UnsafeCell { 65 | #[inline(always)] 66 | pub fn with(&self, f: F) -> R 67 | where 68 | F: FnOnce(*const T) -> R, 69 | { 70 | f(self.0.get()) 71 | } 72 | 73 | #[inline(always)] 74 | pub fn with_mut(&self, f: F) -> R 75 | where 76 | F: FnOnce(*mut T) -> R, 77 | { 78 | f(self.0.get()) 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/raw.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::declare_interior_mutable_const)] 2 | 3 | use core::alloc::Layout; 4 | use core::mem::{self, MaybeUninit}; 5 | use core::ops::Index; 6 | use core::{ptr, slice}; 7 | 8 | use crate::loom::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; 9 | use crate::loom::cell::UnsafeCell; 10 | use crate::loom::AtomicMut; 11 | 12 | use alloc::boxed::Box; 13 | 14 | /// A lock-free, append-only vector. 15 | pub struct Vec { 16 | /// A counter used to retrieve a unique index to push to. 17 | /// 18 | /// This value may be more than the true length as it will 19 | /// be incremented before values are actually stored. 20 | inflight: AtomicUsize, 21 | 22 | /// Buckets of length 32, 64 .. 2^62. 23 | buckets: [Bucket; BUCKETS], 24 | 25 | /// The number of initialized elements in this vector. 26 | count: AtomicUsize, 27 | } 28 | 29 | /// Safety: A `Vec` owns its elements, so sending a 30 | /// vector also sends its elements, hence `T: Send`. 31 | unsafe impl Send for Vec {} 32 | 33 | /// Safety: Sharing a `Vec` only exposes shared access 34 | /// to the elements inside, so we only require `T: Sync`. 35 | unsafe impl Sync for Vec {} 36 | 37 | impl Vec { 38 | /// An empty vector. 39 | #[cfg(not(loom))] 40 | const EMPTY: Vec = Vec { 41 | inflight: AtomicUsize::new(0), 42 | buckets: [Bucket::EMPTY; BUCKETS], 43 | count: AtomicUsize::new(0), 44 | }; 45 | 46 | /// Create an empty vector. 47 | #[cfg(not(loom))] 48 | pub const fn new() -> Vec { 49 | Vec::EMPTY 50 | } 51 | 52 | /// Create an empty vector. 53 | #[cfg(loom)] 54 | pub fn new() -> Vec { 55 | Vec { 56 | inflight: AtomicUsize::new(0), 57 | buckets: [0; BUCKETS].map(|_| Bucket { 58 | entries: AtomicPtr::new(ptr::null_mut()), 59 | }), 60 | count: AtomicUsize::new(0), 61 | } 62 | } 63 | 64 | /// Constructs a new, empty `Vec` with the specified capacity. 65 | #[inline] 66 | pub fn with_capacity(capacity: usize) -> Vec { 67 | let init = match capacity { 68 | 0 => 0, 69 | // Initialize enough buckets for the n'th element to be inserted. 70 | n => Location::of(n - 1).bucket, 71 | }; 72 | 73 | let mut vec = Vec::new(); 74 | for (i, bucket) in vec.buckets[..=init].iter_mut().enumerate() { 75 | // Initialize each bucket. 76 | let len = Location::bucket_capacity(i); 77 | // Safety: `Location::bucket_capacity` is non-zero. 78 | *bucket = Bucket::from_ptr(unsafe { Bucket::alloc(len) }); 79 | } 80 | 81 | vec 82 | } 83 | 84 | /// Returns the number of elements in the vector. 85 | #[inline] 86 | pub fn count(&self) -> usize { 87 | // The `Acquire` here synchronizes with the `Release` increment 88 | // when an entry is added to the vector. 89 | self.count.load(Ordering::Acquire) 90 | } 91 | 92 | /// Returns a reference to the element at the given index. 93 | #[inline] 94 | pub fn get(&self, index: usize) -> Option<&T> { 95 | let location = Location::of(index); 96 | 97 | // Safety: `location.bucket` is always in bounds. 98 | // 99 | // The `Acquire` load here synchronizes with the `Release` 100 | // store in `Vec::get_or_alloc`. 101 | let entries = unsafe { 102 | self.buckets 103 | .get_unchecked(location.bucket) 104 | .entries 105 | .load(Ordering::Acquire) 106 | }; 107 | 108 | // The bucket is uninitialized. 109 | if entries.is_null() { 110 | return None; 111 | } 112 | 113 | // Safety: `location.entry` is always in bounds for its bucket. 114 | let entry = unsafe { &*entries.add(location.entry) }; 115 | 116 | if entry.active.load(Ordering::Acquire) { 117 | // Safety: The entry is active. Additionally, the `Acquire` 118 | // load synchronizes with the `Release` store in `Vec::write`, 119 | // ensuring the initialization happens-before this read. 120 | unsafe { return Some(entry.value_unchecked()) } 121 | } 122 | 123 | // The entry is uninitialized. 124 | None 125 | } 126 | 127 | /// Returns a reference to the element at the given index. 128 | /// 129 | /// # Safety 130 | /// 131 | /// The entry at `index` must be initialized. 132 | #[inline] 133 | pub unsafe fn get_unchecked(&self, index: usize) -> &T { 134 | // Safety: Caller guarantees the bucket and entry are initialized. 135 | unsafe { 136 | let location = Location::of_unchecked(index); 137 | 138 | // The `Acquire` load here synchronizes with the `Release` 139 | // store in `Vec::get_or_alloc`. 140 | let entry = self 141 | .buckets 142 | .get_unchecked(location.bucket) 143 | .entries 144 | .load(Ordering::Acquire) 145 | .add(location.entry); 146 | 147 | (*entry).value_unchecked() 148 | } 149 | } 150 | 151 | /// Returns a mutable reference to the element at the given index. 152 | #[inline] 153 | pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { 154 | let location = Location::of(index); 155 | 156 | // Safety: `location.bucket` is always in bounds. 157 | let entries = unsafe { 158 | self.buckets 159 | .get_unchecked_mut(location.bucket) 160 | .entries 161 | .read_mut() 162 | }; 163 | 164 | // The bucket is uninitialized. 165 | if entries.is_null() { 166 | return None; 167 | } 168 | 169 | // Safety: `location.entry` is always in bounds for its bucket. 170 | let entry = unsafe { &mut *entries.add(location.entry) }; 171 | 172 | if entry.active.read_mut() { 173 | // Safety: The entry is active. 174 | unsafe { return Some(entry.value_unchecked_mut()) } 175 | } 176 | 177 | // The entry is uninitialized. 178 | None 179 | } 180 | 181 | /// Returns a mutable reference to the element at the given index. 182 | /// 183 | /// # Safety 184 | /// 185 | /// The entry at `index` must be initialized. 186 | #[inline] 187 | pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { 188 | // Safety: Caller guarantees the bucket and entry are initialized. 189 | unsafe { 190 | let location = Location::of_unchecked(index); 191 | 192 | let entry = self 193 | .buckets 194 | .get_unchecked_mut(location.bucket) 195 | .entries 196 | .read_mut() 197 | .add(location.entry); 198 | 199 | (*entry).value_unchecked_mut() 200 | } 201 | } 202 | 203 | /// Returns a unique index for insertion. 204 | #[inline] 205 | fn next_index(&self) -> usize { 206 | // The inflight counter cannot exceed `isize::MAX`, allowing it to catch capacity overflow. 207 | // 208 | // Note that the `Relaxed` ordering here is sufficient, as we only care about 209 | // the index being unique and do not use it for synchronization. 210 | let index = self.inflight.fetch_add(1, Ordering::Relaxed); 211 | if index > MAX_INDEX { 212 | // We could alternatively abort here, as `Arc` does. But we decrement and panic instead 213 | // to keep in line with `Vec`'s behavior. Assuming that `isize::MAX` concurrent threads 214 | // don't call this method, it is still impossible for it to overflow. 215 | self.inflight.fetch_sub(1, Ordering::Relaxed); 216 | panic!("capacity overflow"); 217 | } 218 | index 219 | } 220 | 221 | /// Appends the element returned from the closure to the back of the vector 222 | /// at the index represented by the `usize` passed to closure. 223 | /// 224 | /// This allows for use of the would-be index to be utilized within the 225 | /// element. 226 | #[inline] 227 | pub fn push_with(&self, f: F) -> usize 228 | where 229 | F: FnOnce(usize) -> T, 230 | { 231 | // Acquire a unique index to insert into. 232 | let index = self.next_index(); 233 | let value = f(index); 234 | 235 | // Safety: `next_index` is always in-bounds and unique. 236 | unsafe { self.write(index, value) } 237 | } 238 | 239 | /// Appends an element to the back of the vector. 240 | #[inline] 241 | pub fn push(&self, value: T) -> usize { 242 | // Safety: `next_index` is always in-bounds and unique. 243 | unsafe { self.write(self.next_index(), value) } 244 | } 245 | 246 | /// Write an element at the given index. 247 | /// 248 | /// # Safety 249 | /// 250 | /// The index must be unique and in-bounds. 251 | #[inline] 252 | unsafe fn write(&self, index: usize, value: T) -> usize { 253 | // Safety: Caller guarantees the entry is initialized. 254 | let location = unsafe { Location::of_unchecked(index) }; 255 | 256 | // Eagerly allocate the next bucket if we are close to the end of this one. 257 | if index == (location.bucket_len - (location.bucket_len >> 3)) { 258 | if let Some(next_bucket) = self.buckets.get(location.bucket + 1) { 259 | // Safety: Bucket lengths are non-zero. 260 | unsafe { Vec::get_or_alloc(next_bucket, location.bucket_len << 1) }; 261 | } 262 | } 263 | 264 | // Safety: `location.bucket` is always in bounds. 265 | let bucket = unsafe { self.buckets.get_unchecked(location.bucket) }; 266 | 267 | // The `Acquire` load here synchronizes with the `Release` 268 | // store in `Vec::get_or_alloc`. 269 | let mut entries = bucket.entries.load(Ordering::Acquire); 270 | 271 | // The bucket has not been allocated yet. 272 | if entries.is_null() { 273 | // Safety: Bucket lengths are non-zero. 274 | entries = unsafe { Vec::get_or_alloc(bucket, location.bucket_len) }; 275 | } 276 | 277 | unsafe { 278 | // Safety: We loaded the entries pointer with `Acquire` ordering, 279 | // ensuring that it's initialization happens-before our access. 280 | // 281 | // Additionally, `location.entry` is always in bounds for its bucket. 282 | let entry = &*entries.add(location.entry); 283 | 284 | // Safety: We have unique access to this entry. 285 | // 286 | // 1. It is impossible for another thread to attempt a `push` 287 | // to this location as we retrieved it from `next_index`. 288 | // 289 | // 2. Any thread trying to `get` this entry will see `!active` 290 | // and will not try to access it. 291 | entry 292 | .slot 293 | .with_mut(|slot| slot.write(MaybeUninit::new(value))); 294 | 295 | // Let other threads know that this entry is active. 296 | // 297 | // Note that this `Release` write synchronizes with the `Acquire` 298 | // load in `Vec::get`. 299 | entry.active.store(true, Ordering::Release); 300 | } 301 | 302 | // Increase the element count. 303 | // 304 | // The `Release` here is not strictly necessary, but does 305 | // allow users to use `count` for some sort of synchronization 306 | // in terms of the number of initialized elements. 307 | self.count.fetch_add(1, Ordering::Release); 308 | 309 | // Return the index of the entry that we initialized. 310 | index 311 | } 312 | 313 | /// Race to initialize a bucket. 314 | /// 315 | /// The returned pointer is guaranteed to be valid for access. 316 | /// 317 | /// Note that we avoid contention on bucket allocation by having a specified 318 | /// writer eagerly allocate the next bucket. 319 | /// 320 | /// # Safety 321 | /// 322 | /// The provided length must be non-zero. 323 | #[cold] 324 | #[inline(never)] 325 | unsafe fn get_or_alloc(bucket: &Bucket, len: usize) -> *mut Entry { 326 | // Safety: Guaranteed by caller. 327 | let entries = unsafe { Bucket::alloc(len) }; 328 | 329 | match bucket.entries.compare_exchange( 330 | ptr::null_mut(), 331 | entries, 332 | // Establish synchronization with `Acquire` loads of the pointer. 333 | Ordering::Release, 334 | // If we lose the race, ensure that we synchronize with the initialization 335 | // of the bucket that won. 336 | Ordering::Acquire, 337 | ) { 338 | // We won the race. 339 | Ok(_) => entries, 340 | 341 | // We lost the race, deallocate our bucket and return the bucket that won. 342 | Err(found) => unsafe { 343 | Bucket::dealloc(entries, len); 344 | found 345 | }, 346 | } 347 | } 348 | 349 | /// Reserves capacity for at least `additional` more elements to be inserted in 350 | /// the vector. 351 | /// 352 | /// The collection may reserve more space to avoid frequent reallocations. 353 | pub fn reserve(&self, additional: usize) { 354 | let len = self.count.load(Ordering::Acquire); 355 | let mut location = Location::of(len.checked_add(additional).unwrap_or(MAX_INDEX)); 356 | 357 | // Allocate buckets starting from the bucket at `len + additional` and 358 | // working our way backwards. 359 | loop { 360 | // Safety: `location.bucket` is always in bounds. 361 | let bucket = unsafe { self.buckets.get_unchecked(location.bucket) }; 362 | 363 | // Reached an initialized bucket, we're done. 364 | // 365 | // `Relaxed` is sufficient here as we never access the bucket 366 | // and only ensure that it is initialized. 367 | if !bucket.entries.load(Ordering::Relaxed).is_null() { 368 | break; 369 | } 370 | 371 | // Allocate the bucket. 372 | // 373 | // Safety: Bucket lengths are non-zero. 374 | unsafe { Vec::get_or_alloc(bucket, location.bucket_len) }; 375 | 376 | // Reached the first bucket, we're done. 377 | if location.bucket == 0 { 378 | break; 379 | } 380 | 381 | location.bucket -= 1; 382 | location.bucket_len = Location::bucket_capacity(location.bucket); 383 | } 384 | } 385 | 386 | // Returns an iterator over the vector. 387 | #[inline] 388 | pub fn iter(&self) -> Iter { 389 | Iter { 390 | index: 0, 391 | yielded: 0, 392 | location: Location { 393 | bucket: 0, 394 | entry: 0, 395 | bucket_len: Location::bucket_capacity(0), 396 | }, 397 | } 398 | } 399 | 400 | /// Clear every element in the vector. 401 | #[inline] 402 | pub fn clear(&mut self) { 403 | let mut iter = self.iter(); 404 | 405 | // Consume and reset every entry in the vector. 406 | while iter.next_owned(self).is_some() {} 407 | 408 | // Reset the count. 409 | self.count.store(0, Ordering::Relaxed); 410 | self.inflight.store(0, Ordering::Relaxed); 411 | } 412 | } 413 | 414 | impl Index for Vec { 415 | type Output = T; 416 | 417 | #[inline] 418 | fn index(&self, index: usize) -> &Self::Output { 419 | self.get(index) 420 | .unwrap_or_else(|| panic!("index `{index}` is uninitialized")) 421 | } 422 | } 423 | 424 | impl Drop for Vec { 425 | fn drop(&mut self) { 426 | for (i, bucket) in self.buckets.iter_mut().enumerate() { 427 | let entries = bucket.entries.read_mut(); 428 | 429 | if entries.is_null() { 430 | break; 431 | } 432 | 433 | let len = Location::bucket_capacity(i); 434 | 435 | // Safety: We have `&mut self` and verified that this bucket is 436 | // initialized. 437 | unsafe { Bucket::dealloc(entries, len) } 438 | } 439 | } 440 | } 441 | 442 | /// A bucket of entries. 443 | struct Bucket { 444 | entries: AtomicPtr>, 445 | } 446 | 447 | impl Bucket { 448 | /// An empty bucket. 449 | #[cfg(not(loom))] 450 | const EMPTY: Bucket = Bucket { 451 | entries: AtomicPtr::new(ptr::null_mut()), 452 | }; 453 | } 454 | 455 | /// A possibly uninitialized entry in the vector. 456 | struct Entry { 457 | /// A flag indicating whether or not this entry is initialized. 458 | active: AtomicBool, 459 | 460 | /// The entry's value. 461 | slot: UnsafeCell>, 462 | } 463 | 464 | impl Bucket { 465 | /// Create a `Bucket` from the given `Entry` pointer. 466 | fn from_ptr(entries: *mut Entry) -> Bucket { 467 | Bucket { 468 | entries: AtomicPtr::new(entries), 469 | } 470 | } 471 | 472 | /// Allocate an array of entries of the specified length. 473 | /// 474 | /// # Safety 475 | /// 476 | /// The provided length must be non-zero. 477 | #[cfg(not(loom))] 478 | unsafe fn alloc(len: usize) -> *mut Entry { 479 | let layout = Layout::array::>(len).unwrap(); 480 | 481 | // Note that this sets the `active` flag to `false`. 482 | // 483 | // Safety: Caller guarantees that `layout` has a non-zero size, and 484 | // `AtomicBool`, `UnsafeCell`, and `MaybeUninit` are zeroable types. 485 | let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; 486 | 487 | // Handle allocation errors. 488 | if ptr.is_null() { 489 | alloc::alloc::handle_alloc_error(layout); 490 | } 491 | 492 | ptr.cast::>() 493 | } 494 | 495 | /// Allocate an array of entries of the specified length. 496 | #[cfg(loom)] 497 | unsafe fn alloc(len: usize) -> *mut Entry { 498 | // Note we cannot use `alloc_zeroed` for Loom types. 499 | let entries = (0..len) 500 | .map(|_| Entry:: { 501 | slot: UnsafeCell::new(MaybeUninit::uninit()), 502 | active: AtomicBool::new(false), 503 | }) 504 | .collect::]>>(); 505 | 506 | Box::into_raw(entries) as *mut Entry 507 | } 508 | 509 | /// Deallocate a bucket of the specified capacity. 510 | /// 511 | /// # Safety 512 | /// 513 | /// The safety requirements of `slice::from_raw_parts_mut` and 514 | /// `Box::from_raw`. The pointer must be a valid, owned pointer 515 | /// to an array of entries of the provided length. 516 | unsafe fn dealloc(entries: *mut Entry, len: usize) { 517 | // Safety: Guaranteed by caller. 518 | unsafe { drop(Box::from_raw(slice::from_raw_parts_mut(entries, len))) } 519 | } 520 | } 521 | 522 | impl Entry { 523 | /// Returns a reference to the value in this entry. 524 | /// 525 | /// # Safety 526 | /// 527 | /// The value must be initialized. 528 | #[inline] 529 | unsafe fn value_unchecked(&self) -> &T { 530 | // Safety: Guaranteed by caller. 531 | self.slot.with(|slot| unsafe { (*slot).assume_init_ref() }) 532 | } 533 | 534 | /// Returns a mutable reference to the value in this entry. 535 | /// 536 | // # Safety 537 | // 538 | // The value must be initialized. 539 | #[inline] 540 | unsafe fn value_unchecked_mut(&mut self) -> &mut T { 541 | // Safety: Guaranteed by caller. 542 | self.slot 543 | .with_mut(|slot| unsafe { (*slot).assume_init_mut() }) 544 | } 545 | } 546 | 547 | impl Drop for Entry { 548 | fn drop(&mut self) { 549 | if self.active.read_mut() { 550 | // Safety: We have `&mut self` and verifid that the value is initialized. 551 | unsafe { ptr::drop_in_place(self.slot.with_mut(|slot| (*slot).as_mut_ptr())) } 552 | } 553 | } 554 | } 555 | 556 | /// The location of an entry in the bucket array. 557 | #[derive(Debug, Clone)] 558 | struct Location { 559 | /// The index of the bucket. 560 | bucket: usize, 561 | 562 | /// The length of the bucket at the above index. 563 | bucket_len: usize, 564 | 565 | /// The index of the entry in the bucket. 566 | entry: usize, 567 | } 568 | 569 | /// The number of entries that are skipped from the start of a vector. 570 | /// 571 | /// Index calculations assume that buckets are of sizes `[2^0, 2^1, ..., 2^62]`. 572 | /// To skip shorter buckets and avoid unnecessary location, the zeroeth entry 573 | /// index is remapped to a larger index (`2^0 + ... + 2^4 = 31`). 574 | const ZERO_ENTRY: usize = 31; 575 | 576 | /// The number of buckets that are skipped from the start of a vector. 577 | /// 578 | /// This is the index that the zeroeth bucket index is remapped to (`5`). 579 | const ZERO_BUCKET: usize = (usize::BITS - ZERO_ENTRY.leading_zeros()) as usize; 580 | 581 | /// The number of buckets in a vector. 582 | const BUCKETS: usize = (usize::BITS as usize) - 1 - ZERO_BUCKET; 583 | 584 | /// The maximum index of an element in the vector. 585 | /// 586 | /// Note that capacity of the vector is: 587 | /// `2^ZERO_BUCKET + ... + 2^62 = isize::MAX - ZERO_INDEX`. 588 | /// 589 | /// We limit at `isize::MAX` instead of `usize::MAX` to allow checking for overflows in 590 | /// `next_index` using only `fetch_add`. In practice, you won't be able to make a `Vec` with this 591 | /// many elements unless it's a `Vec<()>` (since each entry adds one byte to `T`), and we don't 592 | /// particularly care to support that case. 593 | const MAX_INDEX: usize = (isize::MAX as usize) - ZERO_ENTRY - 1; 594 | 595 | impl Location { 596 | /// Returns the location of a given entry in a vector. 597 | /// 598 | /// This function will panic if the entry is greater than `MAX_INDEX`. 599 | #[inline] 600 | fn of(index: usize) -> Location { 601 | if index > MAX_INDEX { 602 | panic!("index out of bounds"); 603 | } 604 | 605 | Location::of_raw(index + ZERO_ENTRY) 606 | } 607 | 608 | /// Returns the location of a given entry in a vector, without bounds checks. 609 | /// 610 | /// # Safety 611 | /// 612 | /// The index must be in-bounds. 613 | #[inline] 614 | unsafe fn of_unchecked(index: usize) -> Location { 615 | // Note: This can lead to unsoundness if it wraps. 616 | Location::of_raw(index + ZERO_ENTRY) 617 | } 618 | 619 | /// Returns the location of the entry at index `index - ZERO_INDEX` in a vector. 620 | #[inline] 621 | fn of_raw(index: usize) -> Location { 622 | // Calculate the bucket index based on ⌊log2(index)⌋. 623 | let bucket = BUCKETS - ((index + 1).leading_zeros() as usize); 624 | let bucket_len = Location::bucket_capacity(bucket); 625 | 626 | // Offset the absolute index by the capacity of the preceding buckets. 627 | let entry = index - (bucket_len - 1); 628 | 629 | Location { 630 | bucket, 631 | bucket_len, 632 | entry, 633 | } 634 | } 635 | 636 | /// Returns the capacity of the bucket at the given index. 637 | #[inline] 638 | fn bucket_capacity(bucket: usize) -> usize { 639 | 1 << (bucket + ZERO_BUCKET) 640 | } 641 | } 642 | 643 | /// An iterator over the elements of a [`Vec`]. 644 | #[derive(Clone)] 645 | pub struct Iter { 646 | location: Location, 647 | yielded: usize, 648 | index: usize, 649 | } 650 | 651 | impl Iter { 652 | /// Returns a pointer to the next entry in the iterator. 653 | /// 654 | /// Any returned entries are guaranteed to be initialized. 655 | #[inline] 656 | fn next(&mut self, vec: &Vec) -> Option<(usize, *mut Entry)> { 657 | // We returned every entry in the vector, we're done. 658 | if self.yielded == vec.count() { 659 | return None; 660 | } 661 | 662 | // It is possible that the the length was incremented due to an element 663 | // being stored in a bucket that we have already iterated over, so we 664 | // still have to check that we are in bounds. 665 | while self.location.bucket < BUCKETS { 666 | // Safety: Bounds checked above. 667 | let entries = unsafe { 668 | vec.buckets 669 | .get_unchecked(self.location.bucket) 670 | .entries 671 | .load(Ordering::Acquire) 672 | }; 673 | 674 | // Despite this bucket not being initialized, it is possible, but rare, 675 | // that a subsequent bucket was initialized before this one. Thus we 676 | // have to continue checking every bucket until we yield `vec.count()` 677 | // elements. 678 | if !entries.is_null() { 679 | while self.location.entry < self.location.bucket_len { 680 | // Safety: Bounds checked above. 681 | let entry = unsafe { entries.add(self.location.entry) }; 682 | let index = self.index; 683 | 684 | self.location.entry += 1; 685 | self.index += 1; 686 | 687 | // Continue even after we find an uninitialized entry for the same 688 | // reason as uninitialized buckets. 689 | // 690 | // Note that the `Acquire` ordering ensures that the initialization 691 | // of the value happens-before this read if we yield the entry. 692 | if unsafe { (*entry).active.load(Ordering::Acquire) } { 693 | self.yielded += 1; 694 | return Some((index, entry)); 695 | } 696 | } 697 | } 698 | 699 | // We iterated over an entire bucket, move on to the next one. 700 | self.location.entry = 0; 701 | self.location.bucket += 1; 702 | if self.location.bucket < BUCKETS { 703 | self.location.bucket_len = Location::bucket_capacity(self.location.bucket); 704 | } 705 | } 706 | 707 | None 708 | } 709 | 710 | /// Returns a shared reference to the next entry in the iterator. 711 | #[inline] 712 | pub fn next_shared<'v, T>(&mut self, vec: &'v Vec) -> Option<(usize, &'v T)> { 713 | self.next(vec) 714 | // Safety: `Iter::next` guarantees that the entry is initialized. 715 | .map(|(index, entry)| (index, unsafe { (*entry).value_unchecked() })) 716 | } 717 | 718 | /// Returns an owned reference to the next entry in the iterator, resetting the entry in 719 | /// the vector. 720 | #[inline] 721 | pub fn next_owned(&mut self, vec: &mut Vec) -> Option { 722 | self.next(vec).map(|(_, entry)| { 723 | // Safety: `Iter::next` guarantees that the entry is initialized, and we have `&mut Vec`. 724 | let entry = unsafe { &mut *entry }; 725 | 726 | // Mark the entry as uninitialized so it is not accessed after this. 727 | entry.active.write_mut(false); 728 | 729 | // Safety: `Iter::next` only yields initialized entries. 730 | unsafe { 731 | let slot = entry.slot.with_mut(|slot| &mut *slot); 732 | mem::replace(slot, MaybeUninit::uninit()).assume_init() 733 | } 734 | }) 735 | } 736 | 737 | /// Returns the number of elements that have been yielded by this iterator. 738 | #[inline] 739 | pub fn yielded(&self) -> usize { 740 | self.yielded 741 | } 742 | } 743 | 744 | #[cfg(test)] 745 | mod tests { 746 | use super::*; 747 | extern crate std; 748 | 749 | #[test] 750 | fn location() { 751 | assert_eq!(Location::bucket_capacity(0), 32); 752 | for i in 0..32 { 753 | let loc = Location::of(i); 754 | assert_eq!(loc.bucket_len, 32); 755 | assert_eq!(loc.bucket, 0); 756 | assert_eq!(loc.entry, i); 757 | } 758 | 759 | assert_eq!(Location::bucket_capacity(1), 64); 760 | for i in 33..96 { 761 | let loc = Location::of(i); 762 | assert_eq!(loc.bucket_len, 64); 763 | assert_eq!(loc.bucket, 1); 764 | assert_eq!(loc.entry, i - 32); 765 | } 766 | 767 | assert_eq!(Location::bucket_capacity(2), 128); 768 | for i in 96..224 { 769 | let loc = Location::of(i); 770 | assert_eq!(loc.bucket_len, 128); 771 | assert_eq!(loc.bucket, 2); 772 | assert_eq!(loc.entry, i - 96); 773 | } 774 | } 775 | 776 | #[test] 777 | fn max_entries() { 778 | let mut entries = 0; 779 | for i in 0..BUCKETS { 780 | entries += Location::bucket_capacity(i); 781 | } 782 | assert_eq!(entries, MAX_INDEX + 1); 783 | 784 | let max = Location::of(MAX_INDEX); 785 | assert_eq!(max.bucket, BUCKETS - 1); 786 | assert_eq!(max.bucket_len, 1 << (usize::BITS - 2)); 787 | assert_eq!(max.entry, (1 << (usize::BITS - 2)) - 1); 788 | 789 | let panic = std::panic::catch_unwind(|| Location::of(MAX_INDEX + 1)).unwrap_err(); 790 | let panic = *panic.downcast_ref::<&'static str>().unwrap(); 791 | assert_eq!(panic, "index out of bounds"); 792 | } 793 | } 794 | -------------------------------------------------------------------------------- /tests/loom.rs: -------------------------------------------------------------------------------- 1 | #![cfg(loom)] 2 | 3 | use loom::thread; 4 | use std::sync::Arc; 5 | 6 | #[test] 7 | fn write_write() { 8 | loom::model(|| { 9 | let vec = Arc::new(boxcar::Vec::new()); 10 | let v1 = vec.clone(); 11 | let v2 = vec.clone(); 12 | 13 | let t1 = thread::spawn(move || v1.push(1)); 14 | let t2 = thread::spawn(move || v2.push(2)); 15 | 16 | let i1 = t1.join().unwrap(); 17 | let i2 = t2.join().unwrap(); 18 | 19 | assert_eq!(vec[i1], 1); 20 | assert_eq!(vec[i2], 2); 21 | assert_eq!(vec.count(), 2); 22 | }); 23 | } 24 | 25 | #[test] 26 | fn read_write() { 27 | loom::model(|| { 28 | let vec = Arc::new(boxcar::Vec::new()); 29 | let v1 = vec.clone(); 30 | let v2 = vec.clone(); 31 | 32 | let t1 = thread::spawn(move || v1.push(1)); 33 | let t2 = thread::spawn(move || loop { 34 | let Some(&v) = v2.get(0) else { 35 | thread::yield_now(); 36 | continue; 37 | }; 38 | 39 | break v; 40 | }); 41 | 42 | t1.join().unwrap(); 43 | let val = t2.join().unwrap(); 44 | 45 | assert_eq!(val, 1); 46 | assert_eq!(vec.count(), 1); 47 | }); 48 | } 49 | 50 | #[test] 51 | fn mixed() { 52 | loom::model(|| { 53 | let vec = Arc::new(boxcar::Vec::new()); 54 | let v1 = vec.clone(); 55 | let v2 = vec.clone(); 56 | let v3 = vec.clone(); 57 | 58 | let t1 = thread::spawn(move || { 59 | v1.push(0); 60 | }); 61 | 62 | let t2 = thread::spawn(move || { 63 | v2.push(1); 64 | }); 65 | 66 | let t3 = thread::spawn(move || { 67 | for i in 0..2 { 68 | if let Some(&v) = v3.get(i) { 69 | assert!(v == 0 || v == 1); 70 | }; 71 | } 72 | }); 73 | 74 | t1.join().unwrap(); 75 | t2.join().unwrap(); 76 | t3.join().unwrap(); 77 | 78 | assert_eq!(vec.count(), 2); 79 | 80 | let mut values = vec.iter().map(|(_, &v)| v).collect::>(); 81 | values.sort(); 82 | assert_eq!(values, (0..2).collect::>()); 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /tests/vec.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | use std::{sync::Barrier, thread}; 3 | 4 | #[test] 5 | fn simple() { 6 | let vec = boxcar::vec![0, 1, 2]; 7 | assert_eq!(vec[0], 0); 8 | assert_eq!(vec[1], 1); 9 | assert_eq!(vec[2], 2); 10 | 11 | for x in 3..1000 { 12 | let i = vec.push(x); 13 | assert_eq!(vec[i], x); 14 | } 15 | 16 | for i in 0..1000 { 17 | assert_eq!(vec[i], i); 18 | } 19 | 20 | for (i, &x) in vec.iter() { 21 | assert_eq!(i, x); 22 | } 23 | 24 | for (i, x) in vec.into_iter().enumerate() { 25 | assert_eq!(i, x); 26 | } 27 | } 28 | 29 | #[test] 30 | fn simple_boxed() { 31 | let vec = boxcar::vec![Box::new(0), Box::new(1), Box::new(2)]; 32 | assert_eq!(vec[0], Box::new(0)); 33 | assert_eq!(vec[1], Box::new(1)); 34 | assert_eq!(vec[2], Box::new(2)); 35 | 36 | for x in 3..1000 { 37 | let i = vec.push(Box::new(x)); 38 | assert_eq!(*vec[i], x); 39 | } 40 | 41 | for i in 0..1000 { 42 | assert_eq!(*vec[i], i); 43 | } 44 | 45 | for (i, x) in vec.iter() { 46 | assert_eq!(i, **x); 47 | } 48 | 49 | for (i, x) in vec.into_iter().enumerate() { 50 | assert_eq!(i, *x); 51 | } 52 | } 53 | 54 | #[test] 55 | fn clear() { 56 | struct T<'a>(&'a AtomicUsize); 57 | impl Drop for T<'_> { 58 | fn drop(&mut self) { 59 | self.0.fetch_add(1, Ordering::Relaxed); 60 | } 61 | } 62 | 63 | let drops = AtomicUsize::new(0); 64 | 65 | let mut vec = boxcar::Vec::new(); 66 | vec.push(T(&drops)); 67 | vec.push(T(&drops)); 68 | 69 | let first_ptr: *const _ = vec.iter().next().unwrap().1 as _; 70 | 71 | vec.clear(); 72 | assert_eq!(vec.count(), 0); 73 | assert_eq!(vec.iter().count(), 0); 74 | assert_eq!(drops.swap(0, Ordering::Relaxed), 2); 75 | 76 | vec.clear(); 77 | assert_eq!(vec.count(), 0); 78 | assert_eq!(vec.iter().count(), 0); 79 | assert_eq!(drops.load(Ordering::Relaxed), 0); 80 | 81 | vec.push(T(&drops)); 82 | let ptr: *const _ = vec.iter().next().unwrap().1 as _; 83 | assert_eq!(ptr, first_ptr); 84 | 85 | drop(vec); 86 | assert_eq!(drops.load(Ordering::Relaxed), 1); 87 | } 88 | 89 | #[test] 90 | fn stress() { 91 | let vec = boxcar::Vec::new(); 92 | let barrier = Barrier::new(6); 93 | 94 | thread::scope(|s| { 95 | s.spawn(|| { 96 | barrier.wait(); 97 | for i in 0..1000 { 98 | vec.push(i); 99 | } 100 | }); 101 | 102 | s.spawn(|| { 103 | barrier.wait(); 104 | for i in 1000..2000 { 105 | vec.push(i); 106 | } 107 | }); 108 | 109 | s.spawn(|| { 110 | barrier.wait(); 111 | for i in 2000..3000 { 112 | vec.push(i); 113 | } 114 | }); 115 | 116 | s.spawn(|| { 117 | barrier.wait(); 118 | for i in 3000..4000 { 119 | vec.push(i); 120 | } 121 | }); 122 | 123 | s.spawn(|| { 124 | barrier.wait(); 125 | for i in 0..10_000 { 126 | if let Some(&x) = vec.get(i) { 127 | assert!(x < 4000); 128 | } 129 | } 130 | }); 131 | 132 | s.spawn(|| { 133 | barrier.wait(); 134 | for (i, &x) in vec.iter() { 135 | assert!(x < 4000); 136 | assert!(vec[i] < 4000); 137 | } 138 | }); 139 | }); 140 | 141 | assert_eq!(vec.count(), 4000); 142 | let mut sorted = vec.into_iter().collect::>(); 143 | sorted.sort(); 144 | assert_eq!(sorted, (0..4000).collect::>()); 145 | } 146 | --------------------------------------------------------------------------------