├── .clog.toml
├── .envrc
├── .github
├── FUNDING.yml
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── IMPLEMENTATION.md
├── LICENSE
├── README.md
├── benches
└── bench.rs
├── bin
└── loom.sh
├── flake.lock
├── flake.nix
├── rust-toolchain.toml
├── src
├── cfg.rs
├── clear.rs
├── implementation.rs
├── iter.rs
├── lib.rs
├── macros.rs
├── page
│ ├── mod.rs
│ ├── slot.rs
│ └── stack.rs
├── pool.rs
├── shard.rs
├── sync.rs
├── tests
│ ├── custom_config.rs
│ ├── loom_pool.rs
│ ├── loom_slab.rs
│ ├── mod.rs
│ └── properties.rs
└── tid.rs
└── tests
└── reserved_bits_leak.rs
/.clog.toml:
--------------------------------------------------------------------------------
1 | [clog]
2 | # A repository link with the trailing '.git' which will be used to generate
3 | # all commit and issue links
4 | repository = "https://github.com/hawkw/sharded-slab"
5 | # A constant release title
6 | # subtitle = "sharded-slab"
7 |
8 | # specify the style of commit links to generate, defaults to "github" if omitted
9 | link-style = "github"
10 |
11 | # The preferred way to set a constant changelog. This file will be read for old changelog
12 | # data, then prepended to for new changelog data. It's the equivilant to setting
13 | # both infile and outfile to the same file.
14 | #
15 | # Do not use with outfile or infile fields!
16 | #
17 | # Defaults to stdout when omitted
18 | changelog = "CHANGELOG.md"
19 |
20 | # This sets the output format. There are two options "json" or "markdown" and
21 | # defaults to "markdown" when omitted
22 | output-format = "markdown"
23 |
24 | # If you use tags, you can set the following if you wish to only pick
25 | # up changes since your latest tag
26 | from-latest-tag = true
27 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | use flake;
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [hawkw]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: ["main"]
6 | pull_request:
7 |
8 | env:
9 | RUSTFLAGS: -Dwarnings
10 | RUST_BACKTRACE: 1
11 | MSRV: 1.42.0
12 |
13 | jobs:
14 | build:
15 | name: Build (stable, ${{ matrix.target }})
16 | runs-on: ubuntu-latest
17 | strategy:
18 | matrix:
19 | target:
20 | - x86_64-unknown-linux-gnu
21 | - i686-unknown-linux-musl
22 | steps:
23 | - uses: actions/checkout@master
24 | - name: Install toolchain
25 | uses: actions-rs/toolchain@v1
26 | with:
27 | profile: minimal
28 | toolchain: stable
29 | target: ${{ matrix.target }}
30 | override: true
31 | - name: cargo build --target ${{ matrix.target }}
32 | uses: actions-rs/cargo@v1
33 | with:
34 | command: build
35 | args: --all-targets --target ${{ matrix.target }}
36 |
37 | build-msrv:
38 | name: Build (MSRV)
39 | runs-on: ubuntu-latest
40 | steps:
41 | - uses: actions/checkout@master
42 | - name: Install toolchain
43 | uses: actions-rs/toolchain@v1
44 | with:
45 | profile: minimal
46 | toolchain: ${{ env.MSRV }}
47 | override: true
48 | - name: cargo +${{ env.MSRV }} build
49 | uses: actions-rs/cargo@v1
50 | with:
51 | command: build
52 | env:
53 | RUSTFLAGS: "" # remove -Dwarnings
54 |
55 | build-nightly:
56 | name: Build (nightly)
57 | runs-on: ubuntu-latest
58 | steps:
59 | - uses: actions/checkout@master
60 | - name: Install toolchain
61 | uses: actions-rs/toolchain@v1
62 | with:
63 | profile: minimal
64 | toolchain: nightly
65 | override: true
66 | - name: cargo +nightly build
67 | uses: actions-rs/cargo@v1
68 | with:
69 | command: build
70 | env:
71 | RUSTFLAGS: "" # remove -Dwarnings
72 |
73 | test:
74 | name: Tests (stable)
75 | needs: build
76 | runs-on: ubuntu-latest
77 | steps:
78 | - uses: actions/checkout@master
79 | - name: Install toolchain
80 | uses: actions-rs/toolchain@v1
81 | with:
82 | profile: minimal
83 | toolchain: stable
84 | override: true
85 | - name: Run tests
86 | run: cargo test
87 |
88 | test-loom:
89 | name: Loom tests (stable)
90 | needs: build
91 | runs-on: ubuntu-latest
92 | steps:
93 | - uses: actions/checkout@master
94 | - name: Install toolchain
95 | uses: actions-rs/toolchain@v1
96 | with:
97 | profile: minimal
98 | toolchain: stable
99 | override: true
100 | - name: Run Loom tests
101 | run: ./bin/loom.sh
102 |
103 | clippy:
104 | name: Clippy (stable)
105 | runs-on: ubuntu-latest
106 | steps:
107 | - uses: actions/checkout@v2
108 | - name: Install toolchain
109 | uses: actions-rs/toolchain@v1
110 | with:
111 | profile: minimal
112 | toolchain: stable
113 | components: clippy
114 | override: true
115 | - name: cargo clippy --all-targets --all-features
116 | uses: actions-rs/clippy-check@v1
117 | with:
118 | token: ${{ secrets.GITHUB_TOKEN }}
119 | args: --all-targets --all-features
120 |
121 | rustfmt:
122 | name: Rustfmt (stable)
123 | runs-on: ubuntu-latest
124 | steps:
125 | - uses: actions/checkout@v2
126 | - name: Install toolchain
127 | uses: actions-rs/toolchain@v1
128 | with:
129 | profile: minimal
130 | toolchain: stable
131 | components: rustfmt
132 | override: true
133 | - name: Run rustfmt
134 | uses: actions-rs/cargo@v1
135 | with:
136 | command: fmt
137 | args: -- --check
138 |
139 | all-systems-go:
140 | name: "all systems go!"
141 | needs:
142 | - build
143 | - build-msrv
144 | # Note: we explicitly *don't* require the `build-nightly` job to pass,
145 | # since the nightly Rust compiler is unstable. We don't want nightly
146 | # regressions to break our build --- this CI job is intended for
147 | # informational reasons rather than as a gatekeeper for merging PRs.
148 | - test
149 | - test-loom
150 | - clippy
151 | - rustfmt
152 | runs-on: ubuntu-latest
153 | steps:
154 | - run: exit 0
155 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - v[0-9]+.*
7 |
8 | jobs:
9 | create-release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: taiki-e/create-gh-release-action@v1
14 | with:
15 | # Path to changelog.
16 | changelog: CHANGELOG.md
17 | # Reject releases from commits not contained in branches
18 | # that match the specified pattern (regular expression)
19 | branch: main
20 | env:
21 | # (Required) GitHub token for creating GitHub Releases.
22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | Cargo.lock
3 | .direnv
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ### v0.1.7 (2023-10-04)
3 |
4 |
5 | #### Bug Fixes
6 |
7 | * index out of bounds in `get()` and `get_owned()` (#88) ([fdbc930f](https://github.com/hawkw/sharded-slab/commit/fdbc930fb14b0f6f8b77cd6efdad5a1bdf8d3c04))
8 | * **unique_iter:** prevent panics if a slab is empty (#88) ([bd599e0b](https://github.com/hawkw/sharded-slab/commit/bd599e0b2a60a953f25f27ba1fa86682150e05c2), closes [#73](https://github.com/hawkw/sharded-slab/issues/73))
9 |
10 |
11 |
12 |
13 | ## 0.1.6 (2023-09-27)
14 |
15 |
16 | #### Features
17 |
18 | * publicly export `UniqueIter` (#87) ([e4d6482d](https://github.com/hawkw/sharded-slab/commit/e4d6482db05d5767b47eae1b0217faad30f2ebd5), closes [#77](https://github.com/hawkw/sharded-slab/issues/77))
19 |
20 | #### Bug Fixes
21 |
22 | * use a smaller `CustomConfig` for 32-bit tests (#84) ([828ffff9](https://github.com/hawkw/sharded-slab/commit/828ffff9f82cfc41ed66b4743563c4dddc97c1ce), closes [#82](https://github.com/hawkw/sharded-slab/issues/82))
23 |
24 |
25 |
26 |
27 | ## 0.1.5 (2023-08-28)
28 |
29 |
30 | #### Bug Fixes
31 |
32 | * **Slab:** invalid generation in case of custom config (#80) ([ca090279](https://github.com/hawkw/sharded-slab/commit/ca09027944812d024676029a3dde62d27ef22015))
33 |
34 |
35 |
36 |
37 | ### 0.1.4 (2021-10-12)
38 |
39 |
40 | #### Features
41 |
42 | * emit a nicer panic when thread count overflows `MAX_SHARDS` (#64) ([f1ed058a](https://github.com/hawkw/sharded-slab/commit/f1ed058a3ee296eff033fc0fb88f62a8b2f83f10))
43 |
44 |
45 |
46 |
47 | ### 0.1.3 (2021-08-02)
48 |
49 |
50 | #### Bug Fixes
51 |
52 | * set up MSRV in CI (#61) ([dfcc9080](https://github.com/hawkw/sharded-slab/commit/dfcc9080a62d08e359f298a9ffb0f275928b83e4), closes [#60](https://github.com/hawkw/sharded-slab/issues/60))
53 | * **tests:** duplicate `hint` mod defs with loom ([0ce3fd91](https://github.com/hawkw/sharded-slab/commit/0ce3fd91feac8b4edb4f1ece6aebfc4ba4e50026))
54 |
55 |
56 |
57 |
58 | ### 0.1.2 (2021-08-01)
59 |
60 |
61 | #### Bug Fixes
62 |
63 | * make debug assertions drop safe ([26d35a69](https://github.com/hawkw/sharded-slab/commit/26d35a695c9e5d7c62ab07cc5e66a0c6f8b6eade))
64 |
65 | #### Features
66 |
67 | * improve panics on thread ID bit exhaustion ([9ecb8e61](https://github.com/hawkw/sharded-slab/commit/9ecb8e614f107f68b5c6ba770342ae72af1cd07b))
68 |
69 |
70 |
71 |
72 | ## 0.1.1 (2021-1-4)
73 |
74 |
75 | #### Bug Fixes
76 |
77 | * change `loom` to an optional dependency ([9bd442b5](https://github.com/hawkw/sharded-slab/commit/9bd442b57bc56153a67d7325144ebcf303e0fe98))
78 |
79 |
80 | ## 0.1.0 (2020-10-20)
81 |
82 |
83 | #### Bug Fixes
84 |
85 | * fix `remove` and `clear` returning true when the key is stale ([b52d38b2](https://github.com/hawkw/sharded-slab/commit/b52d38b2d2d3edc3a59d3dba6b75095bbd864266))
86 |
87 | #### Breaking Changes
88 |
89 | * **Pool:** change `Pool::create` to return a mutable guard (#48) ([778065ea](https://github.com/hawkw/sharded-slab/commit/778065ead83523e0a9d951fbd19bb37fda3cc280), closes [#41](https://github.com/hawkw/sharded-slab/issues/41), [#16](https://github.com/hawkw/sharded-slab/issues/16))
90 | * **Slab:** rename `Guard` to `Entry` for consistency ([425ad398](https://github.com/hawkw/sharded-slab/commit/425ad39805ee818dc6b332286006bc92c8beab38))
91 |
92 | #### Features
93 |
94 | * add missing `Debug` impls ([71a8883f](https://github.com/hawkw/sharded-slab/commit/71a8883ff4fd861b95e81840cb5dca167657fe36))
95 | * **Pool:**
96 | * add `Pool::create_owned` and `OwnedRefMut` ([f7774ae0](https://github.com/hawkw/sharded-slab/commit/f7774ae0c5be99340f1e7941bde62f7044f4b4d8))
97 | * add `Arc::get_owned` and `OwnedRef` ([3e566d91](https://github.com/hawkw/sharded-slab/commit/3e566d91e1bc8cc4630a8635ad24b321ec047fe7), closes [#29](https://github.com/hawkw/sharded-slab/issues/29))
98 | * change `Pool::create` to return a mutable guard (#48) ([778065ea](https://github.com/hawkw/sharded-slab/commit/778065ead83523e0a9d951fbd19bb37fda3cc280), closes [#41](https://github.com/hawkw/sharded-slab/issues/41), [#16](https://github.com/hawkw/sharded-slab/issues/16))
99 | * **Slab:**
100 | * add `Arc::get_owned` and `OwnedEntry` ([53a970a2](https://github.com/hawkw/sharded-slab/commit/53a970a2298c30c1afd9578268c79ccd44afba05), closes [#29](https://github.com/hawkw/sharded-slab/issues/29))
101 | * rename `Guard` to `Entry` for consistency ([425ad398](https://github.com/hawkw/sharded-slab/commit/425ad39805ee818dc6b332286006bc92c8beab38))
102 | * add `slab`-style `VacantEntry` API ([6776590a](https://github.com/hawkw/sharded-slab/commit/6776590adeda7bf4a117fb233fc09cfa64d77ced), closes [#16](https://github.com/hawkw/sharded-slab/issues/16))
103 |
104 | #### Performance
105 |
106 | * allocate shard metadata lazily (#45) ([e543a06d](https://github.com/hawkw/sharded-slab/commit/e543a06d7474b3ff92df2cdb4a4571032135ff8d))
107 |
108 |
109 |
110 |
111 | ### 0.0.9 (2020-04-03)
112 |
113 |
114 | #### Features
115 |
116 | * **Config:** validate concurrent refs ([9b32af58](9b32af58), closes [#21](21))
117 | * **Pool:**
118 | * add `fmt::Debug` impl for `Pool` ([ffa5c7a0](ffa5c7a0))
119 | * add `Default` impl for `Pool` ([d2399365](d2399365))
120 | * add a sharded object pool for reusing heap allocations (#19) ([89734508](89734508), closes [#2](2), [#15](15))
121 | * **Slab::take:** add exponential backoff when spinning ([6b743a27](6b743a27))
122 |
123 | #### Bug Fixes
124 |
125 | * incorrect wrapping when overflowing maximum ref count ([aea693f3](aea693f3), closes [#22](22))
126 |
127 |
128 |
129 |
130 | ### 0.0.8 (2020-01-31)
131 |
132 |
133 | #### Bug Fixes
134 |
135 | * `remove` not adding slots to free lists ([dfdd7aee](dfdd7aee))
136 |
137 |
138 |
139 |
140 | ### 0.0.7 (2019-12-06)
141 |
142 |
143 | #### Bug Fixes
144 |
145 | * **Config:** compensate for 0 being a valid TID ([b601f5d9](b601f5d9))
146 | * **DefaultConfig:**
147 | * const overflow on 32-bit ([74d42dd1](74d42dd1), closes [#10](10))
148 | * wasted bit patterns on 64-bit ([8cf33f66](8cf33f66))
149 |
150 |
151 |
152 |
153 | ## 0.0.6 (2019-11-08)
154 |
155 |
156 | #### Features
157 |
158 | * **Guard:** expose `key` method #8 ([748bf39b](748bf39b))
159 |
160 |
161 |
162 |
163 | ## 0.0.5 (2019-10-31)
164 |
165 |
166 | #### Performance
167 |
168 | * consolidate per-slot state into one AtomicUsize (#6) ([f1146d33](f1146d33))
169 |
170 | #### Features
171 |
172 | * add Default impl for Slab ([61bb3316](61bb3316))
173 |
174 |
175 |
176 |
177 | ## 0.0.4 (2019-21-30)
178 |
179 |
180 | #### Features
181 |
182 | * prevent items from being removed while concurrently accessed ([872c81d1](872c81d1))
183 | * added `Slab::remove` method that marks an item to be removed when the last thread
184 | accessing it finishes ([872c81d1](872c81d1))
185 |
186 | #### Bug Fixes
187 |
188 | * nicer handling of races in remove ([475d9a06](475d9a06))
189 |
190 | #### Breaking Changes
191 |
192 | * renamed `Slab::remove` to `Slab::take` ([872c81d1](872c81d1))
193 | * `Slab::get` now returns a `Guard` type ([872c81d1](872c81d1))
194 |
195 |
196 |
197 | ## 0.0.3 (2019-07-30)
198 |
199 |
200 | #### Bug Fixes
201 |
202 | * split local/remote to fix false sharing & potential races ([69f95fb0](69f95fb0))
203 | * set next pointer _before_ head ([cc7a0bf1](cc7a0bf1))
204 |
205 | #### Breaking Changes
206 |
207 | * removed potentially racy `Slab::len` and `Slab::capacity` methods ([27af7d6c](27af7d6c))
208 |
209 |
210 | ## 0.0.2 (2019-03-30)
211 |
212 |
213 | #### Bug Fixes
214 |
215 | * fix compilation failure in release mode ([617031da](617031da))
216 |
217 |
218 |
219 | ## 0.0.1 (2019-02-30)
220 |
221 | - Initial release
222 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "sharded-slab"
3 | version = "0.1.7"
4 | authors = ["Eliza Weisman "]
5 | edition = "2018"
6 | documentation = "https://docs.rs/sharded-slab/"
7 | homepage = "https://github.com/hawkw/sharded-slab"
8 | repository = "https://github.com/hawkw/sharded-slab"
9 | readme = "README.md"
10 | rust-version = "1.42.0"
11 | license = "MIT"
12 | keywords = ["slab", "allocator", "lock-free", "atomic"]
13 | categories = ["memory-management", "data-structures", "concurrency"]
14 | description = """
15 | A lock-free concurrent slab.
16 | """
17 | exclude = [
18 | "flake.nix",
19 | "flake.lock",
20 | ".envrc",
21 | ".clog.toml",
22 | ".cargo",
23 | ".github",
24 | ".direnv",
25 | "bin",
26 | ]
27 |
28 | [badges]
29 | maintenance = { status = "experimental" }
30 |
31 | [[bench]]
32 | name = "bench"
33 | harness = false
34 |
35 | [dependencies]
36 | lazy_static = "1"
37 |
38 | [dev-dependencies]
39 | proptest = "1"
40 | criterion = "0.3"
41 | slab = "0.4.2"
42 | memory-stats = "1"
43 | indexmap = "1" # newer versions lead to "candidate versions found which didn't match" on 1.42.0
44 |
45 | [target.'cfg(loom)'.dependencies]
46 | loom = { version = "0.5", features = ["checkpoint"], optional = true }
47 |
48 | [target.'cfg(loom)'.dev-dependencies]
49 | loom = { version = "0.5", features = ["checkpoint"] }
50 |
51 | [package.metadata.docs.rs]
52 | all-features = true
53 | rustdoc-args = ["--cfg", "docsrs"]
54 |
55 | [lints.rust]
56 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)', 'cfg(slab_print)'] }
57 |
--------------------------------------------------------------------------------
/IMPLEMENTATION.md:
--------------------------------------------------------------------------------
1 | Notes on `sharded-slab`'s implementation and design.
2 |
3 | # Design
4 |
5 | The sharded slab's design is strongly inspired by the ideas presented by
6 | Leijen, Zorn, and de Moura in [Mimalloc: Free List Sharding in
7 | Action][mimalloc]. In this report, the authors present a novel design for a
8 | memory allocator based on a concept of _free list sharding_.
9 |
10 | Memory allocators must keep track of what memory regions are not currently
11 | allocated ("free") in order to provide them to future allocation requests.
12 | The term [_free list_][freelist] refers to a technique for performing this
13 | bookkeeping, where each free block stores a pointer to the next free block,
14 | forming a linked list. The memory allocator keeps a pointer to the most
15 | recently freed block, the _head_ of the free list. To allocate more memory,
16 | the allocator pops from the free list by setting the head pointer to the
17 | next free block of the current head block, and returning the previous head.
18 | To deallocate a block, the block is pushed to the free list by setting its
19 | first word to the current head pointer, and the head pointer is set to point
20 | to the deallocated block. Most implementations of slab allocators backed by
21 | arrays or vectors use a similar technique, where pointers are replaced by
22 | indices into the backing array.
23 |
24 | When allocations and deallocations can occur concurrently across threads,
25 | they must synchronize accesses to the free list; either by putting the
26 | entire allocator state inside of a lock, or by using atomic operations to
27 | treat the free list as a lock-free structure (such as a [Treiber stack]). In
28 | both cases, there is a significant performance cost — even when the free
29 | list is lock-free, it is likely that a noticeable amount of time will be
30 | spent in compare-and-swap loops. Ideally, the global synchronzation point
31 | created by the single global free list could be avoided as much as possible.
32 |
33 | The approach presented by Leijen, Zorn, and de Moura is to introduce
34 | sharding and thus increase the granularity of synchronization significantly.
35 | In mimalloc, the heap is _sharded_ so that each thread has its own
36 | thread-local heap. Objects are always allocated from the local heap of the
37 | thread where the allocation is performed. Because allocations are always
38 | done from a thread's local heap, they need not be synchronized.
39 |
40 | However, since objects can move between threads before being deallocated,
41 | _deallocations_ may still occur concurrently. Therefore, Leijen et al.
42 | introduce a concept of _local_ and _global_ free lists. When an object is
43 | deallocated on the same thread it was originally allocated on, it is placed
44 | on the local free list; if it is deallocated on another thread, it goes on
45 | the global free list for the heap of the thread from which it originated. To
46 | allocate, the local free list is used first; if it is empty, the entire
47 | global free list is popped onto the local free list. Since the local free
48 | list is only ever accessed by the thread it belongs to, it does not require
49 | synchronization at all, and because the global free list is popped from
50 | infrequently, the cost of synchronization has a reduced impact. A majority
51 | of allocations can occur without any synchronization at all; and
52 | deallocations only require synchronization when an object has left its
53 | parent thread (a relatively uncommon case).
54 |
55 | [mimalloc]: https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf
56 | [freelist]: https://en.wikipedia.org/wiki/Free_list
57 | [Treiber stack]: https://en.wikipedia.org/wiki/Treiber_stack
58 |
59 | # Implementation
60 |
61 | A slab is represented as an array of [`MAX_THREADS`] _shards_. A shard
62 | consists of a vector of one or more _pages_ plus associated metadata.
63 | Finally, a page consists of an array of _slots_, head indices for the local
64 | and remote free lists.
65 |
66 | ```text
67 | ┌─────────────┐
68 | │ shard 1 │
69 | │ │ ┌─────────────┐ ┌────────┐
70 | │ pages───────┼───▶│ page 1 │ │ │
71 | ├─────────────┤ ├─────────────┤ ┌────▶│ next──┼─┐
72 | │ shard 2 │ │ page 2 │ │ ├────────┤ │
73 | ├─────────────┤ │ │ │ │XXXXXXXX│ │
74 | │ shard 3 │ │ local_head──┼──┘ ├────────┤ │
75 | └─────────────┘ │ remote_head─┼──┐ │ │◀┘
76 | ... ├─────────────┤ │ │ next──┼─┐
77 | ┌─────────────┐ │ page 3 │ │ ├────────┤ │
78 | │ shard n │ └─────────────┘ │ │XXXXXXXX│ │
79 | └─────────────┘ ... │ ├────────┤ │
80 | ┌─────────────┐ │ │XXXXXXXX│ │
81 | │ page n │ │ ├────────┤ │
82 | └─────────────┘ │ │ │◀┘
83 | └────▶│ next──┼───▶ ...
84 | ├────────┤
85 | │XXXXXXXX│
86 | └────────┘
87 | ```
88 |
89 |
90 | The size of the first page in a shard is always a power of two, and every
91 | subsequent page added after the first is twice as large as the page that
92 | preceeds it.
93 |
94 | ```text
95 |
96 | pg.
97 | ┌───┐ ┌─┬─┐
98 | │ 0 │───▶ │ │
99 | ├───┤ ├─┼─┼─┬─┐
100 | │ 1 │───▶ │ │ │ │
101 | ├───┤ ├─┼─┼─┼─┼─┬─┬─┬─┐
102 | │ 2 │───▶ │ │ │ │ │ │ │ │
103 | ├───┤ ├─┼─┼─┼─┼─┼─┼─┼─┼─┬─┬─┬─┬─┬─┬─┬─┐
104 | │ 3 │───▶ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
105 | └───┘ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
106 | ```
107 |
108 | When searching for a free slot, the smallest page is searched first, and if
109 | it is full, the search proceeds to the next page until either a free slot is
110 | found or all available pages have been searched. If all available pages have
111 | been searched and the maximum number of pages has not yet been reached, a
112 | new page is then allocated.
113 |
114 | Since every page is twice as large as the previous page, and all page sizes
115 | are powers of two, we can determine the page index that contains a given
116 | address by shifting the address down by the smallest page size and
117 | looking at how many twos places necessary to represent that number,
118 | telling us what power of two page size it fits inside of. We can
119 | determine the number of twos places by counting the number of leading
120 | zeros (unused twos places) in the number's binary representation, and
121 | subtracting that count from the total number of bits in a word.
122 |
123 | The formula for determining the page number that contains an offset is thus:
124 |
125 | ```rust,ignore
126 | WIDTH - ((offset + INITIAL_PAGE_SIZE) >> INDEX_SHIFT).leading_zeros()
127 | ```
128 |
129 | where `WIDTH` is the number of bits in a `usize`, and `INDEX_SHIFT` is
130 |
131 | ```rust,ignore
132 | INITIAL_PAGE_SIZE.trailing_zeros() + 1;
133 | ```
134 |
135 | [`MAX_THREADS`]: https://docs.rs/sharded-slab/latest/sharded_slab/trait.Config.html#associatedconstant.MAX_THREADS
136 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Eliza Weisman
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
11 | all 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
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sharded-slab
2 |
3 | A lock-free concurrent slab.
4 |
5 | [![Crates.io][crates-badge]][crates-url]
6 | [![Documentation][docs-badge]][docs-url]
7 | [![CI Status][ci-badge]][ci-url]
8 | [![GitHub License][license-badge]][license]
9 | ![maintenance status][maint-badge]
10 |
11 | [crates-badge]: https://img.shields.io/crates/v/sharded-slab.svg
12 | [crates-url]: https://crates.io/crates/sharded-slab
13 | [docs-badge]: https://docs.rs/sharded-slab/badge.svg
14 | [docs-url]: https://docs.rs/sharded-slab/latest
15 | [ci-badge]: https://github.com/hawkw/sharded-slab/workflows/CI/badge.svg
16 | [ci-url]: https://github.com/hawkw/sharded-slab/actions?workflow=CI
17 | [license-badge]: https://img.shields.io/crates/l/sharded-slab
18 | [license]: LICENSE
19 | [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg
20 |
21 | Slabs provide pre-allocated storage for many instances of a single data
22 | type. When a large number of values of a single type are required,
23 | this can be more efficient than allocating each item individually. Since the
24 | allocated items are the same size, memory fragmentation is reduced, and
25 | creating and removing new items can be very cheap.
26 |
27 | This crate implements a lock-free concurrent slab, indexed by `usize`s.
28 |
29 | **Note**: This crate is currently experimental. Please feel free to use it in
30 | your projects, but bear in mind that there's still plenty of room for
31 | optimization, and there may still be some lurking bugs.
32 |
33 | ## Usage
34 |
35 | First, add this to your `Cargo.toml`:
36 |
37 | ```toml
38 | sharded-slab = "0.1.7"
39 | ```
40 |
41 | This crate provides two types, [`Slab`] and [`Pool`], which provide slightly
42 | different APIs for using a sharded slab.
43 |
44 | [`Slab`] implements a slab for _storing_ small types, sharing them between
45 | threads, and accessing them by index. New entries are allocated by [inserting]
46 | data, moving it in by value. Similarly, entries may be deallocated by [taking]
47 | from the slab, moving the value out. This API is similar to a `Vec