Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
9d72f79
fix(thread_aware): probe relocation destination under a shared lock
sandersaares Aug 14, 2026
31b7b45
test(thread_aware): report the storm benchmark as a median over workers
sandersaares Aug 14, 2026
a609a80
test(thread_aware): separate contention from oversubscription in the …
sandersaares Aug 14, 2026
ba9fe5a
test(thread_aware): make the relocation storm benchmark measure conte…
sandersaares Aug 14, 2026
8465e67
test(thread_aware): bracket the storm benchmark's timed region with load
sandersaares Aug 14, 2026
5fb8616
test(thread_aware): measure concurrent relocation as batched throughput
sandersaares Aug 17, 2026
735f8f3
perf(thread_aware): partition relocation storage by affinity
sandersaares Aug 17, 2026
fcbc74c
refactor(thread_aware): hide storage behind an opaque SharedStorage h…
sandersaares Aug 17, 2026
1b6ff35
test(thread_aware): drop the vestigial unused_results allow in the cg…
sandersaares Aug 18, 2026
a0c8aaa
test(thread_aware): drop unnecessary lint allows from the relocation …
sandersaares Aug 18, 2026
9ca3a69
docs(thread_aware): tighten the relocation-locking and benchmark prose
sandersaares Aug 18, 2026
bb4e350
refactor(thread_aware): guarantee slot locks never poison, and rename…
sandersaares Aug 18, 2026
ebb5d30
refactor(thread_aware): clarify relocation docs and skip redundant fa…
sandersaares Aug 18, 2026
891c3f0
test(thread_aware): cover the source-slot restore against mutation
sandersaares Aug 18, 2026
ce84329
chore(thread_aware): bump to 0.10.0 for the breaking storage API change
sandersaares Aug 18, 2026
9395c8c
feat(thread_aware): make Storage publicly constructible and fillable
sandersaares Aug 18, 2026
fdb8131
test(thread_aware): use unwrap over expect in concurrent relocation test
sandersaares Aug 18, 2026
5a404a0
test(thread_aware): cover Storage::default and the slot-count guard
sandersaares Aug 18, 2026
a9de8a8
fix(thread_aware): assert slot invariant after releasing the write lock
sandersaares Aug 18, 2026
2ad7cf3
fix(thread_aware): saturate strong_count against a concurrent-relocat…
sandersaares Aug 18, 2026
34bda02
fix(thread_aware): gate derive-using bench and test targets on the de…
sandersaares Aug 18, 2026
fe35f4e
docs(thread_aware): add design.md and align claims with real guarantees
sandersaares Aug 19, 2026
df08d3b
fix(thread_aware)!: keep the carried value on a same-slot relocation
sandersaares Aug 19, 2026
da67ae5
test(thread_aware): isolate the cross-slot miss in the miss benchmark
sandersaares Aug 19, 2026
665d5db
fix(thread_aware): soft-pass out-of-range slot index in release
sandersaares Aug 19, 2026
5d17d48
perf(thread_aware): back the growable slot table with a lock-free vector
sandersaares Aug 19, 2026
2674518
refactor(thread_aware): replace the boxcar slot vector with a OnceLoc…
sandersaares Aug 21, 2026
da9acf0
feat(thread_aware): observe out-of-range affinity via a metric
sandersaares Aug 21, 2026
90239aa
Merge remote-tracking branch 'origin/main' into u/sasaares/fix-thread…
sandersaares Aug 21, 2026
a39b063
test(thread_aware): catch the out-of-range affinity metric mutation
sandersaares Aug 21, 2026
44f1d03
refactor(thread_aware): make out-of-range relocation a no-op
sandersaares Aug 21, 2026
2495b45
test(thread_aware): cover out-of-range relocation branches
sandersaares Aug 21, 2026
a1c7630
docs(thread_aware): clarify relocation holds one slot lock at a time
sandersaares Aug 21, 2026
c86f3ff
refactor(thread_aware): NonZero strategy count, Arc submodule, storag…
sandersaares Aug 21, 2026
12968d3
refactor(thread_aware): share bench object model; refine bench prose
sandersaares Aug 21, 2026
8fb43ba
docs(thread_aware): document NonZero count and direct-access panic co…
sandersaares Aug 21, 2026
18ed517
Merge remote-tracking branch 'origin/main' into u/sasaares/fix-thread…
sandersaares Aug 21, 2026
1948325
fix(thread_aware): repair private intra-doc link and cover probe hit …
sandersaares Aug 21, 2026
ed7b4c1
refactor(thread_aware): adopt write-once OnceLock slot cells
sandersaares Aug 21, 2026
a514ff1
test(thread_aware): pin the sourceless same-slot relocation arm
sandersaares Aug 21, 2026
275ef36
docs(thread_aware): align benchmark comments with lock-free cells
sandersaares Aug 21, 2026
b8385c6
docs(thread_aware): correct hit-path prose and slot-table wording
sandersaares Aug 24, 2026
13651e5
fix(thread_aware): run relocation factory once per affinity under a race
sandersaares Aug 24, 2026
14b1eb9
refactor(thread_aware): return Result from Storage::insert
sandersaares Aug 24, 2026
5a30d97
fix(thread_aware): document slot initialization constraints
sandersaares Aug 24, 2026
94e555a
fix(thread_aware): make adopter race test mutation-safe
sandersaares Aug 24, 2026
a34ad18
docs(thread_aware): clarify affinitized constructor behavior
sandersaares Aug 24, 2026
f2b23fc
fix(thread_aware): record source before relocation fast paths
sandersaares Aug 24, 2026
cc5be07
fix(thread_aware): address relocation review findings
sandersaares Aug 24, 2026
ca2286d
docs(thread_aware): clarify Strategy Arc contract
sandersaares Aug 24, 2026
838e267
perf(thread_aware): avoid recounting initialized slots
sandersaares Aug 24, 2026
1224e78
refactor(thread_aware): isolate relocation benchmarks
sandersaares Aug 25, 2026
bd358df
fix(thread_aware): correct hit-path benchmark handoffs
sandersaares Aug 25, 2026
3427d57
ci(thread_aware): exclude benchmark support from test metrics
sandersaares Aug 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .spelling
Original file line number Diff line number Diff line change
Expand Up @@ -837,3 +837,9 @@ reportable
substrings
unrepresentable
unterminated
underflow
underflowing
initializations
poisonable
affinitized
unmaterialized
14 changes: 13 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ templated_uri_macros = { path = "crates/templated_uri_macros", default-features
templated_uri_macros_impl = { path = "crates/templated_uri_macros_impl", default-features = false, version = "0.4.0" }
testing_aids = { path = "crates/testing_aids", default-features = false }
thiserror = { version = "2.0.17", default-features = false }
thread_aware = { path = "crates/thread_aware", default-features = false, version = "0.9.0" }
thread_aware = { path = "crates/thread_aware", default-features = false, version = "0.10.0" }
thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.7.5" }
thread_aware_macros_impl = { path = "crates/thread_aware_macros_impl", default-features = false, version = "0.7.4" }
thread_local = { version = "1.1.8", default-features = false }
Expand Down
4 changes: 2 additions & 2 deletions crates/anyspawn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ contention-free, NUMA-friendly task dispatch.
This crate was developed as part of <a href="https://github.com/microsoft/oxidizer">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/anyspawn">source code</a>.
</sub>

[__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQb11VxC_uAPOQbtUn4Wx2-BfAbid3Nt1Y27Pobprn8Z6FjFy9hYvRhcoQblHE7Bl8YSN4bb97k0EOW-rkbZQa-GdodS-cbCkeYjGZgZ-BhZIKCaGFueXNwYXduZTAuNy4wgmx0aHJlYWRfYXdhcmVlMC45LjA
[__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQb11VxC_uAPOQbtUn4Wx2-BfAbid3Nt1Y27Pobprn8Z6FjFy9hYvRhcoQblHE7Bl8YSN4bb97k0EOW-rkbZQa-GdodS-cbCkeYjGZgZ-BhZIKCaGFueXNwYXduZTAuNy4wgmx0aHJlYWRfYXdhcmVmMC4xMC4w
[__link0]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=Spawner
[__link1]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=SpawnCustom
[__link2]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=CustomSpawnerBuilder
[__link3]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=ThreadAware
[__link3]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=ThreadAware
[__link4]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=SpawnCustom
[__link5]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=Spawner::new_tokio
[__link6]: https://docs.rs/anyspawn/0.7.0/anyspawn/?search=Spawner::new_tokio_with_handle
3 changes: 2 additions & 1 deletion crates/automation/src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ pub const INTERNAL_CRATES: &[&str] = &[
"automation",
"benchmarking",
"observed_testing",
"testing_aids",
"rest_over_grpc_examples",
"rest_over_grpc_tests",
"testing_aids",
"thread_aware_benchmarking",
];

/// Run a cargo command and pipe the output to stdout/stderr
Expand Down
17 changes: 15 additions & 2 deletions crates/thread_aware/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[package]
name = "thread_aware"
description = "Facilities to support thread-isolated state."
version = "0.9.0"
version = "0.10.0"
readme = "README.md"
keywords = ["oxidizer", "thread", "aware"]
categories = ["data-structures", "no-std"]
Expand Down Expand Up @@ -58,7 +58,7 @@ all-features = true
default = ["std", "derive"]
# Enables the per-affinity Arc and hosted-only type impls. Disable for `#![no_std]`
# environments; the crate then requires only `alloc`.
std = []
std = ["dep:nm"]
derive = ["dep:thread_aware_macros"]
threads = ["std", "dep:many_cpus"]
Comment thread
sandersaares marked this conversation as resolved.

Expand Down Expand Up @@ -88,6 +88,7 @@ uuid = ["dep:uuid"]
[dependencies]
many_cpus = { workspace = true, optional = true }

nm = { workspace = true, optional = true }
thread_aware_macros = { workspace = true, optional = true }

bytes = { version = "1.11.1", default-features = false, optional = true }
Expand All @@ -103,6 +104,7 @@ http = { version = "1.4.1", default-features = false, features = ["std"] }
jiff = { version = "0.2.21", default-features = false }
many_cpus = { workspace = true, features = ["test-util"] }
mutants = { workspace = true }
nm = { workspace = true }
static_assertions = { workspace = true }
thread_aware_macros = { path = "../thread_aware_macros" }
uuid = { version = "1.21.0", default-features = false }
Expand All @@ -127,6 +129,17 @@ name = "gungraun_third_party"
harness = false
required-features = ["bytes", "http", "jiff02", "uuid"]

# These integration tests exercise `#[derive(ThreadAware)]`, so they compile only
# when the `derive` feature is enabled. Gating them keeps every additive feature
# combination (for example `--no-default-features --features std`) buildable.
[[test]]
name = "derive_compiles"
required-features = ["derive"]

[[test]]
name = "transfer_pass"
required-features = ["derive"]

# >>> anvil-managed: anvil-lints
[lints]
workspace = true
Expand Down
54 changes: 27 additions & 27 deletions crates/thread_aware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Essential building blocks for thread-per-core libraries.

## Crate features

* The **`std` Cargo feature** *(enabled by default)* enables the per-affinity `Arc` and
* The **`std` Cargo feature** *(enabled by default)* enables the strategy-partitioned `Arc` and
hosted-only type implementations.
* **`derive`** *(default)* re-exports the `#[derive(ThreadAware)]` macro.
* **`threads`** enables the `registry` module and implies `std`.
Expand Down Expand Up @@ -108,7 +108,7 @@ from types that implement [`ThreadAware`][__link24].

## Features

* The **`std` Cargo feature** *(enabled by default)* enables the per-affinity `Arc` and
* The **`std` Cargo feature** *(enabled by default)* enables the strategy-partitioned `Arc` and
hosted-only type implementations. Disable it for `#![no_std]` environments; the crate then
requires `alloc` and pointer-width atomics.
* **`derive`** *(default)*: Re-exports the `#[derive(ThreadAware)]` macro from the companion
Expand Down Expand Up @@ -195,36 +195,36 @@ impl Service {
This crate was developed as part of <a href="https://github.com/microsoft/oxidizer">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/thread_aware">source code</a>.
</sub>

[__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQb11VxC_uAPOQbtUn4Wx2-BfAbid3Nt1Y27Pobprn8Z6FjFy9hYvRhcoQbUpEHEN0wyqMbT69q7SZ3jhAb-itdqiIRp1EbkJGs8OCz_nBhZIKCbHRocmVhZF9hd2FyZWUwLjkuMIJzdGhyZWFkX2F3YXJlX21hY3Jvc2UwLjcuNQ
[__link0]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link1]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__cargo_doc2readme_dependencies_info]: ggGmYW0CYXZlMC43LjJhdIQb11VxC_uAPOQbtUn4Wx2-BfAbid3Nt1Y27Pobprn8Z6FjFy9hYvRhcoQbUbHFZqBf9MsbIdlZrkyww6sbNpUTMOOpy3sbXQ2vhYWhjORhZIKCbHRocmVhZF9hd2FyZWYwLjEwLjCCc3RocmVhZF9hd2FyZV9tYWNyb3NlMC43LjU
[__link0]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link1]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link10]: https://doc.rust-lang.org/stable/std/marker/trait.Send.html
[__link11]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link11]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link12]: https://doc.rust-lang.org/stable/std/marker/trait.Send.html
[__link13]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link14]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link15]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=Unaware
[__link16]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=Unaware
[__link17]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link18]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link19]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link13]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link14]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link15]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=Unaware
[__link16]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=Unaware
[__link17]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link18]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link19]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link2]: https://doc.rust-lang.org/stable/std/clone/trait.Clone.html
[__link20]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link20]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link21]: https://doc.rust-lang.org/stable/std/?search=thread::spawn
[__link22]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link22]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link23]: https://doc.rust-lang.org/stable/alloc/?search=sync::Arc
[__link24]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link25]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link24]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link25]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link26]: https://docs.rs/thread_aware_macros/0.7.5/thread_aware_macros/?search=ThreadAware
[__link27]: https://docs.rs/thread_aware_macros/0.7.5/thread_aware_macros/?search=ThreadAware
[__link28]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link29]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link3]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link30]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=Arc
[__link31]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=Arc
[__link4]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link5]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link6]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=Arc
[__link28]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link29]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link3]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link30]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=Arc
[__link31]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=Arc
[__link4]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link5]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
[__link6]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=Arc
[__link7]: https://doc.rust-lang.org/stable/alloc/?search=sync::Arc
[__link8]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=storage::Strategy
[__link9]: https://docs.rs/thread_aware/0.9.0/thread_aware/?search=core::ThreadAware
[__link8]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=storage::Strategy
[__link9]: https://docs.rs/thread_aware/0.10.0/thread_aware/?search=core::ThreadAware
134 changes: 134 additions & 0 deletions crates/thread_aware/docs/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# `thread_aware` design

This document describes the user-visible behavior and design tenets of
`thread_aware::Arc` and its companion `storage::Storage` handle. The
`ThreadAware` trait and the relocation model these build on are introduced in
the crate root documentation; this document focuses on the strategy-partitioned
`Arc`. The internal mechanism — the write-once partition slots and the
relocation protocol built on them — is documented separately in
[implementation.md](implementation.md).

## 1. Purpose

`thread_aware::Arc<T, S>` is a shared pointer, like `std::sync::Arc`, that
additionally keeps a distinct value per strategy partition. It lets a type that
is not itself `ThreadAware` — a third-party client, a connection pool, any value
that benefits from being local to the core or NUMA node using it — be shared
through a handle that reacts to relocation by adopting, or lazily creating, the
value belonging to the destination affinity's partition.

Cloning an `Arc<T, S>` is cheap and shares state. Dereferencing it yields the
value carried by that holder. The `Strategy` type parameter `S` decides how
affinities map to partitions.

## 2. Affinities and strategies

An affinity identifies the placement of the code that holds the `Arc`. The
strategy maps that affinity to the partition whose value the holder sees:

- `PerProcess` maps every affinity to one partition, so all clones share a single value
process-wide and relocation keeps that shared value: an `Arc<T, PerProcess>`
behaves like a plain `sync::Arc<T>`.
- `PerCore` defines one partition per processor. A holder that relocates to another
processor observes that processor partition's value.
- `PerNuma` defines one partition per memory region, so holders on cores of the
same NUMA node share a value and holders on different nodes do not.

Custom strategies are possible; they are expected to report a partition count —
always at least one — that is consistent across the affinities that share one
`Arc` (see [implementation.md](implementation.md)).

## 3. Strategy-partitioned values

An `Arc<T, S>` always carries a current value in hand and derefs to it directly,
without synchronization. It additionally keeps the value materialized for each
strategy partition; a holder that relocates into a partition adopts that
partition's value, and clones in the same partition share one underlying
`sync::Arc<T>`.

Constructors create the initial carried value eagerly. Additional partition
values are then produced lazily — a partition is materialized the first time a
holder relocates into it across a partition boundary while it is still empty —
and how a value is produced depends on the constructor used:

- `new` / `new_boxed` run a constructor function once per partition, giving each
partition a freshly built, independent value. Neither requires `T: Clone` or
`T: ThreadAware`.
- `new_with` runs a closure that may capture other `ThreadAware` state, which is
itself relocated for the destination before the value is built.
- `from_unaware` takes one value and clones it for each partition.
- `with_clone_fn` takes a concrete value plus a clone function, so trait-object
values can be reproduced per partition without an object-safe `Clone`.

Materialization runs while the destination partition is being initialized.
Constructor functions, clone functions, and captured `ThreadAware` state must not
relocate an `Arc` backed by the same storage into that partition or form a cycle
among partition initializations. Write-once initialization is non-reentrant, so
such dependencies can deadlock.

A relocation whose source and destination resolve to the same partition is not a
cross-partition move: the holder keeps the value it is already carrying rather than
producing a new one. This is why every relocation under `PerProcess` — where all
affinities share one partition — preserves the shared value.

Relocation is a cooperative performance optimization, not a guarantee: consistent
with the crate-wide contract for `ThreadAware`, a holder that reaches a new
affinity without a relocation call still functions correctly, keeping the value it
already carries rather than switching. Dereferencing never blocks.

`Arc::strong_count` estimates how many strong references to the holder's current
value are held outside the shared storage: it is that value's raw `sync::Arc`
strong count minus the references the storage's partitions hold. It samples those two
counts separately, so under concurrent relocation the result is approximate, and
it saturates rather than underflowing.

## 4. Unsized values

`T` may be unsized, so `Arc<dyn Trait, S>` and other trait-object or slice values
are supported. Because an unsized value cannot be passed or held by value, the
unsized-capable entry points work through a pointer: `new_boxed` and
`with_clone_fn` produce `Box<T>`, and `from_storage` and `Storage::insert` accept
a ready `sync::Arc<T>`. This support is a deliberate, retained capability of the
type; see [implementation.md](implementation.md) for how it shapes the slot
representation.

## 5. Prepared storage

`storage::Storage<T, S>` is the strategy-partitioned table an `Arc` shares across
its clones, exposed as a handle a caller can build directly. This serves the case
where the partition values are known in advance rather than materialized lazily
on relocation:

1. Build an empty table with `Storage::new`.
2. Publish a `sync::Arc<T>` for each strategy partition by passing a representative
affinity to `Storage::insert`; read one back with `Storage::get`.
3. Hand the table to `Arc::from_storage` together with the current affinity to
obtain an `Arc` backed by those values.

`from_storage` requires that the table already hold a value for the current
affinity's partition. An `Arc` built this way that later relocates into an
affinity whose partition the table left empty behaves like a plain `sync::Arc`,
keeping the value it carries. The handle exposes only the affinity-keyed
insert/get surface; the partition layout behind it is not part of the contract.

`Storage::insert` and `Storage::get` require an affinity that maps into the table's
coordinate space — one within the partition count the strategy reports. A caller
building storage by hand controls its own affinities, so these accessors reject
an out-of-range affinity rather than tolerate it.

## 6. Design tenets

- **Performance first, correctness always.** The type exists to reduce
cross-affinity contention, but relocation is advisory: skipping it degrades
performance, never correctness.
- **Cheap, lock-free reads.** Dereferencing yields the carried value directly and
does not synchronize; synchronization is confined to relocation.
- **Per-partition values, in-partition sharing.** Lazily materialized values are
kept per partition and are not shared across partitions, while holders in one
partition share its value. Prepared storage is under the caller's control and
can deliberately place the same value in several partitions.
- **Unsized support is a feature.** Trait-object and slice values are a retained
capability, weighed against representations that would trade `?Sized` away.
- **Storage is usable, its shape is not exposed.** Callers can construct and fill
a `Storage`, but only through an affinity-keyed contract that leaves the
representation free to change.
Loading
Loading