docs(thread_aware): add a thread-aware authoring guide - #742
docs(thread_aware): add a thread-aware authoring guide#742Pato Sandaña (psandana) wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
_documentation is currently exported unconditionally, which unintentionally commits it as part of the crate’s normal public API instead of keeping it doc/test-only like other crates’ _documentation modules.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds a task-oriented authoring guide for thread_aware by introducing a dedicated _documentation module intended to render on docs.rs and complement the crate’s existing reference-style docs.
Changes:
- Exposes a new
_documentationmodule fromthread_awareto host longer-form guidance. - Adds a comprehensive authoring guide covering implementation choices, common pitfalls, testing patterns, and debugging/telemetry.
File summaries
| File | Description |
|---|---|
| crates/thread_aware/src/lib.rs | Exposes the new _documentation module from the crate root. |
| crates/thread_aware/src/_documentation/mod.rs | New documentation module containing the authoring guide and doctest examples. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #742 +/- ##
======================================
Coverage 99.9% 100.0%
======================================
Files 634 634
Lines 84746 84746
======================================
+ Hits 84744 84746 +2
+ Misses 2 0 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
|
||
| /// A guide to authoring thread-aware types: how to implement, test, and debug them, and the | ||
| /// anti-patterns to avoid. See [the guide](_documentation). | ||
| pub mod _documentation; |
There was a problem hiding this comment.
- this should go into thread_aware_core
- rename to just
documentation(also could you apply the same rename across all our crates) - protect with
#[cfg(any(doc, test))]so this module doesn't become part of our public API
There was a problem hiding this comment.
Thanks! I've gated it with #[cfg(any(doc, test))] (705d1a9) so the module is doc/test-only and out of the normal public API, matching the existing _documentation modules in recoverable and fetch.
On the other two points:
- Move to
thread_aware_core: this guide is written around thethread_awarefacade —#[derive(ThreadAware)],Unaware, the strategy-partitionedArc,ThreadBuilder— none of which live inthread_aware_core. Placing it there would needthread_awareas a dev-dependency ofcore(which risks the cyclic-deps gate) and would break the intra-doc links to those facade types. Keeping it where those APIs live seems most useful to readers. If you'd instead like a separate, trait-contract-focused guide incore, I'm happy to split it — just let me know. - Rename
_documentation→documentationacross all crates: glad to, but since every crate currently uses the_documentationname it's a repo-wide convention change; I'd rather land it as its own sweep so it isn't tangled with this guide. I'll open a follow-up unless you'd prefer it here.
There was a problem hiding this comment.
Gated with #[cfg(any(doc, test))] (705d1a9). Opened AB#7857350 for the repo-wide _documentation -> documentation rename so it can land as its own sweep. On the core move: this guide leans on the thread_aware facade - the derive, Unaware, strategy Arc, Relocator - none of which live in thread_aware_core, so moving it there breaks those intra-doc links (and would need thread_aware as a dev-dependency of core, risking the cyclic-deps gate). Happy to instead split out a separate, trait-contract-focused guide for core if you'd prefer.
There was a problem hiding this comment.
Update on the "move to thread_aware_core" point: I've switched the guide's references to the facade types (the ThreadAware derive, Unaware, Arc, Relocator) to docs.rs links (9760e99), matching the pattern thread_aware_core already uses for these same types ([arc]: https://docs.rs/thread_aware/latest/thread_aware/struct.Arc.html, [derive]: .../derive.ThreadAware.html).
That removes the blocker I raised earlier: the guide no longer needs any of those types in scope, so it could move to thread_aware_core without a dev-dependency on thread_aware (and the dependency cycle that would create). It also fixes the feature-gated-link breakage — Arc is std-gated and Relocator is test-utils-gated, so intra-doc links to them broke under --no-default-features; docs.rs links resolve in every config. The core types it still links by intra-doc (ThreadAware, Thread, relocate) resolve from either crate.
Happy to move the module to thread_aware_core in this PR if you'd prefer, or land it here and move it in the rename sweep (AB#7857350). Let me know which you'd like.
There was a problem hiding this comment.
🔵 Needs a closer look
The new _documentation module export and its links should be aligned with the workspace’s doc/test-only _documentation pattern and avoid broken intra-doc links under default feature docs.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:230
- This doc comment links to
Relocatorvia an intra-doc link (crate::Relocator), butRelocatoris behind thetest-utilsfeature in this crate. When building docs with default features (withouttest-utils), this becomes a broken intra-doc link.
Consider referring to it as code (or conditionally documenting it) so the guide doesn’t produce broken links in default doc builds.
crates/thread_aware/src/lib.rs:181
_documentationmodules in this workspace are typically doc/test-only so they render on docs.rs and in doctests without becoming part of the crate’s normal public API. Other crates gate the public_documentationmodule with#[cfg(any(doc, test))](e.g., crates/recoverable/src/lib.rs:81-82, crates/fetch/src/lib.rs:860-862), butthread_awarecurrently exports it unconditionally here. Also, the(_documentation)markdown link is a relative URL and will resolve incorrectly when this doc comment is rendered on the module’s own page.
Consider gating the module and dropping the relative self-link (or converting it to an intra-doc link) to match the established pattern.
/// A guide to authoring thread-aware types: how to implement, test, and debug them, and the
/// anti-patterns to avoid. See [the guide](_documentation).
pub mod _documentation;
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Addresses review feedback on PR #742: export the `_documentation` module only for rustdoc and tests, matching the established pattern in `recoverable` and `fetch`, so it does not become part of the crate's public API in normal builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The new guide contains a couple of documentation correctness issues (notably conflicting bounds semantics and missing std feature context for Arc) that should be reconciled before publishing.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:90
- This section claims the derive bounds the relocated field type (e.g. emitting
where Vec<T>: ThreadAware), but the derive macro's own docs incrates/thread_aware/src/lib.rscurrently describe per-parameterT: ThreadAwarebounds (see "# Generic Bounds" around lib.rs:211-217). To avoid contradicting the crate’s existing reference docs, consider rephrasing this section to describe bounds in terms of the derive’s traversal and link to the authoritative rules.
crates/thread_aware/src/_documentation/mod.rs:122 Arc/PerThread/PerProcessare only exported when thestdfeature is enabled (seecrates/thread_aware/src/lib.rs:263-266), but the decision table currently presents them without that constraint. Adding an explicit "(stdfeature)" note would make the guidance accurate forno_stdusers reading this guide.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
| //! | ||
| //! ## Prefer the derive | ||
| //! | ||
| //! In almost all cases, implement [`ThreadAware`](crate::ThreadAware) with the derive macro. It |
There was a problem hiding this comment.
could we link to actual macro (in external crate)
There was a problem hiding this comment.
Done in 53ca92f - now links to macro@crate::ThreadAware.
| //! call. The lessons in [Anti-patterns](#anti-patterns) are drawn from migrating a large | ||
| //! production service onto an Oxidizer-backed runtime. | ||
| //! | ||
| //! # Why thread-awareness exists |
There was a problem hiding this comment.
this should just link to main lib.rs docs, too much duplication
There was a problem hiding this comment.
Trimmed to a pointer at the crate-level Theory of Operation (53ca92f); dropped the duplication.
| //! "this field does not implement `ThreadAware` yet" - reach for [`Unaware`](crate::Unaware) or | ||
| //! [`Arc`](crate::Arc) for that, so the intent is visible in the type. | ||
| //! | ||
| //! ## What the generated bounds mean |
There was a problem hiding this comment.
this might be too much detail, I myself had trouble grasping what is this traying to say
There was a problem hiding this comment.
Agreed - cut it down to a short pointer at the derive's Generic Bounds reference (53ca92f).
| //! (`PhantomData<fn(*const T)>`), owe no bound at all. See | ||
| //! [the derive's reference](crate::ThreadAware#generic-bounds) for the full rules. | ||
| //! | ||
| //! ## Implementing the trait by hand |
There was a problem hiding this comment.
I am missing thread_aware::Arc guide here, when to implement this. (when we want to maintain separated PerThread instances)
There was a problem hiding this comment.
Added a "Per-worker state with Arc" section (53ca92f): when to reach for Arc<T, PerThread> to keep separate per-worker instances, vs PerProcess / PerNumaNode.
| //! Relocating a subtree while its parent was built from a stale clone (see above) is how affinity | ||
| //! goes stale in practice. | ||
| //! | ||
| //! # Testing |
There was a problem hiding this comment.
check thread_aware::Relocator (under "test-util") that could be used for relocation testing. Also too much detail too, make it more concise.
There was a problem hiding this comment.
Made Testing more concise and now lead with the test-utils Relocator helper for driving relocations (53ca92f).
| //! `test-utils` feature additionally offers a [`Relocator`](crate::Relocator) helper for driving | ||
| //! relocations in tests. | ||
| //! | ||
| //! # Debugging and telemetry |
There was a problem hiding this comment.
we don't have any answer for this right now, I would just omit this section
There was a problem hiding this comment.
Removed the Debugging and telemetry section (53ca92f).
Per @martintmk's review of PR #742: - Trim "Why thread-awareness exists" to a pointer at the crate-level Theory of Operation instead of duplicating it. - Link "the derive macro" to the actual macro (`macro@crate::ThreadAware`). - Simplify "What the generated bounds mean" - defer the detail to the derive's Generic Bounds reference rather than restating it. - Add a "Per-worker state with `Arc`" section explaining when to reach for `Arc<T, PerThread>` (separate per-worker instances) vs `PerProcess`/`PerNumaNode`. - Make "Testing" more concise and lead with the `test-utils` `Relocator` helper. - Drop "Debugging and telemetry" - the telemetry story is not defined yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new docs introduce several feature-gated / derive-macro intra-doc links that will be broken or misleading in non-all-features doc builds and should be adjusted before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
crates/thread_aware/src/lib.rs:180
- The module doc comment uses a Markdown URL link (
[the guide](_documentation)), which is easy to interpret as a relative URL rather than a rustdoc intra-doc link. Since this comment is on the_documentationmodule itself, the extra link is also redundant; removing it avoids brittle/ambiguous linking.
crates/thread_aware/src/_documentation/mod.rs:79
- This section header link targets the derive macro docs; it should use the
derive@disambiguator (notmacro@) to match how derive macros are linked elsewhere in the repo and to avoid ambiguity with non-derive macros.
//! [Generic Bounds](macro@crate::ThreadAware#generic-bounds) reference has the rules.
crates/thread_aware/src/_documentation/mod.rs:121
- These table rows link to
crate::Arc, which isstd-feature gated. In non-stddoc builds this becomes a broken intra-doc link and the table reads as ifArcwere always available. Consider marking these rows asstd-only and using inline code instead of intra-doc links.
//! | Shared state that should differ per worker | [`Arc<T, PerThread>`](crate::Arc) | Materializes a separate `T` per destination. |
//! | Shared state that is the same everywhere | [`Arc<T, PerProcess>`](crate::Arc) | Behaves as a vanilla `Arc`. |
- Files reviewed: 2/2 changed files
- Comments generated: 3
- Review effort level: Lite
…e configs Addresses Copilot review comments on PR #742: - Use the `derive@` disambiguator for the derive-macro links (matches the repo convention, e.g. `internity`), replacing `macro@`. - The `Arc` strategy section pointed at `crate::Arc` / `crate::PerThread` / `crate::PerNumaNode`, which are `std`-gated, so the links broke under `--no-default-features`. Name the `std` feature and drop the feature-gated intra-doc links in favour of plain code spans. - `Relocator` is `test-utils`-gated; its intra-doc link broke in doc builds without that feature. Reword to a plain code span. Doctests still pass under default and all-feature builds; the guide no longer contributes any broken-intra-doc-link warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The new guide contains feature-gated intra-doc usage (crate::Arc, #[derive(ThreadAware)]) that should be shimmed/worded to remain correct across the crate’s supported feature sets.
Review details
Suppressed comments (4)
Previously missed (2) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:46
- This example uses
#[derive(ThreadAware)], but the derive macro is feature-gated (pub use ::thread_aware_macros::ThreadAwareis behindfeature = "derive"). Wrapping the doctest body in a hidden#[cfg(feature = "derive")]shim keeps the guide’s examples copy/paste-able across feature sets (and matches the crate-level docs’ pattern).
This issue also appears in the following locations of the same file:
- line 58
- line 197
crates/thread_aware/src/_documentation/mod.rs:72
Arcis only exported when thestdfeature is enabled (seelib.rs’s#[cfg(any(test, feature = "std"))] pub use ... Arc). Linking to it here makes this paragraph only correct instdbuilds; prefer plain code and explicitly mention thestdfeature, as you do later in the guide.
crates/thread_aware/src/_documentation/mod.rs:62
- This doctest also depends on the
derivefeature for#[derive(ThreadAware)]. Consider gating the doctest body with a hidden#[cfg(feature = "derive")]block so the documentation remains valid whenderiveis disabled.
//! ```rust
//! use thread_aware::ThreadAware;
//!
//! #[derive(ThreadAware)]
//! struct Request {
crates/thread_aware/src/_documentation/mod.rs:201
- This doctest uses
#[derive(ThreadAware)]forUnderTest, which is unavailable when thederivefeature is off. Gating just the derived portion with a hidden#[cfg(feature = "derive")]shim keeps the rest of the example (theTrackerobservation pattern) visible while making the doctest resilient to different feature sets.
//! #[derive(ThreadAware)]
//! struct UnderTest {
//! tracked: Tracker,
//! #[thread_aware(skip)]
//! skipped: Tracker,
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Thanks — addressed in c90d9f4:
|
There was a problem hiding this comment.
🔵 Needs a closer look
Several documented APIs and behaviors do not match the current implementation, and some doctests need feature-gated shims.
Review details
Suppressed comments (7)
Previously missed (4) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:82
- At this revision, the derive still adds
ThreadAwaredirectly to each reached generic parameter (crates/thread_aware_macros_impl/src/lib.rs:121-133), and the public derive reference describes that same parameter-bound model (crates/thread_aware/src/lib.rs:168-174). The field-type-bound behavior described here is therefore not what this checkout implements, so the guide will be stale if it lands before #740; either land the implementation change with this documentation or describe the current behavior.
crates/thread_aware/src/_documentation/mod.rs:100 Vec::new()does not by itself place the next allocation on the destination NUMA node; the core trait documentation notes thatVeccannot choose a node and that dropping only gives an allocator a chance to place future memory (crates/thread_aware_core/src/thread_aware.rs:115-118). This example currently presents NUMA-local reallocation as a guarantee even though it ignoresdestinationand stores no NUMA state. Please describe this as discarding a cache, or show the allocator/state needed for NUMA placement.
crates/thread_aware/src/_documentation/mod.rs:125thread_awaredoes not export anArc(seecrates/thread_aware/src/lib.rs:218-229); the strategy pointer isperformables::arc::Arc(crates/performables/src/arc/mod.rs:150-165). The documentedPerNumaNodename is also not defined—the strategy isPerNuma(crates/performables/src/arc/mod.rs:30-36)—and that crate has nostdfeature (crates/performables/Cargo.toml:29-40). As written, this sends readers to a nonexistentthread_aware::ArcAPI and strategy. Please update this section and the table to nameperformables::arc::{Arc, PerThread, PerProcess, PerNuma}(or remove the section if this guide is not meant to refer to that crate).
crates/thread_aware/src/_documentation/mod.rs:137- The PR description promises a Debugging & telemetry section covering
*.thread_mismatch-style warnings and why their absence is not proof of correctness, but this module has no such section or example; it only says that most failures produce no warning. Please add the promised guidance or remove it from the description so the published guide's scope matches the PR.
crates/thread_aware/src/_documentation/mod.rs:144
- The trait is open-ended and does not require affinity to be stored in a field updated only by
relocate; the core contract explicitly allows implementations to share or detach state (crates/thread_aware_core/src/thread_aware.rs:33-45). ThusCloneis not universally guaranteed to copy affinity verbatim. Please qualify this section to types whoseClonepreserves the state thatrelocateupdates.
//! This is the one to internalize first. A thread-aware type stores its affinity in a field that
//! only [`relocate`](crate::ThreadAware::relocate) mutates. **`Clone` copies that stored affinity
//! verbatim.** Cloning a value that was built on worker A and using the clone on worker B does not
//! move it to B - it is still bound to A, quietly, until something calls `relocate`.
crates/thread_aware/src/_documentation/mod.rs:36
- These examples (and the
Request/UnderTestblocks below) invoke#[derive(ThreadAware)]unconditionally, but this macro is re-exported only undercfg(any(test, feature = "derive"))(crates/thread_aware/src/lib.rs:218-219). Since_documentationitself is included undercfg(doc)(crates/thread_aware/src/lib.rs:138-139), the guide needs the hidden feature-off shims required bydocs/feature-gated-doctests.mdso the examples also compile whenderiveis disabled.
//! #[derive(ThreadAware)]
crates/thread_aware/src/_documentation/mod.rs:31
- The derive does not forward relocation to fields marked
#[thread_aware(skip)]; it moves those fields without invokingrelocate(as described in the next section and implemented by the derive). Saying it forwards to “every field” here is misleading—please qualify this as every non-skipped field.
//! [the derive macro](https://docs.rs/thread_aware/latest/thread_aware/derive.ThreadAware.html). It
//! generates a [`relocate`](crate::ThreadAware::relocate) that forwards the notification to every
//! field, which is exactly what a compound type owes its parts:
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Addresses review feedback on PR #742: export the `_documentation` module only for rustdoc and tests, matching the established pattern in `recoverable` and `fetch`, so it does not become part of the crate's public API in normal builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per @martintmk's review of PR #742: - Trim "Why thread-awareness exists" to a pointer at the crate-level Theory of Operation instead of duplicating it. - Link "the derive macro" to the actual macro (`macro@crate::ThreadAware`). - Simplify "What the generated bounds mean" - defer the detail to the derive's Generic Bounds reference rather than restating it. - Add a "Per-worker state with `Arc`" section explaining when to reach for `Arc<T, PerThread>` (separate per-worker instances) vs `PerProcess`/`PerNumaNode`. - Make "Testing" more concise and lead with the `test-utils` `Relocator` helper. - Drop "Debugging and telemetry" - the telemetry story is not defined yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e configs Addresses Copilot review comments on PR #742: - Use the `derive@` disambiguator for the derive-macro links (matches the repo convention, e.g. `internity`), replacing `macro@`. - The `Arc` strategy section pointed at `crate::Arc` / `crate::PerThread` / `crate::PerNumaNode`, which are `std`-gated, so the links broke under `--no-default-features`. Name the `std` feature and drop the feature-gated intra-doc links in favour of plain code spans. - `Relocator` is `test-utils`-gated; its intra-doc link broke in doc builds without that feature. Reword to a plain code span. Doctests still pass under default and all-feature builds; the guide no longer contributes any broken-intra-doc-link warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9760e99 to
0c0bc1b
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Address the outstanding doctest, NUMA wording, test invocation, and advertised telemetry-content issues.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (6)
crates/thread_aware/src/_documentation/mod.rs:101
Vec::new()does not itself place memory on the destination NUMA node. The core trait documentation explicitly notes thatVeccannot choose a node and that placement comes from the allocator (crates/thread_aware_core/src/thread_aware.rs:115-118), so this example overstates what the shown implementation guarantees. Please describe this as giving a NUMA-aware allocator an opportunity to choose a destination-local allocation instead.
//! // The scratch buffer belonged to the previous worker; drop it so the next use
//! // re-allocates in the destination's NUMA node instead of reaching across.
//! self.buffer = Vec::new();
crates/thread_aware/src/_documentation/mod.rs:144
- This presents copied affinity as a property of every thread-aware type, but the
ThreadAwarecontract neither requiresClonenor requires an affinity field; it explicitly allows implementations to retain, replace, or detach optimization state (crates/thread_aware_core/src/thread_aware.rs:38-45). Qualify this anti-pattern to types whoseCloneimplementation copies affinity-bearing state, so readers do not infer a guarantee that the trait does not make.
//! This is the one to internalize first. A thread-aware type stores its affinity in a field that
//! only [`relocate`](crate::ThreadAware::relocate) mutates. **`Clone` copies that stored affinity
//! verbatim.** Cloning a value that was built on worker A and using the clone on worker B does not
//! move it to B - it is still bound to A, quietly, until something calls `relocate`.
crates/thread_aware/src/_documentation/mod.rs:113
Arcis not part ofthread_aware: the strategy-based implementation isperformables::arcwithPerThread,PerNuma, andPerProcess(crates/performables/src/arc/mod.rs:26-36), andPerNumaNodeis not a repository type. The current wording also says the destination value is created on first use, but this implementation materializes it duringrelocate(crates/performables/src/arc/mod.rs:418-475). Please point readers at the correct crate and names and describe when materialization occurs; the decision table below needs the same correction.
//! pool you do not want contended across cores - wrap it in the strategy-partitioned `Arc<T, S>`
//! ([`thread_aware::Arc`](https://docs.rs/thread_aware/latest/thread_aware/struct.Arc.html), with
//! the crate's `std` feature). With the `PerThread` strategy,
//! relocation materializes a separate `T` for the destination worker (lazily, on first use there),
//! so the sharing is per-worker instead of process-wide. Use `PerProcess`, which behaves as a
crates/thread_aware/src/_documentation/mod.rs:82
- This guide describes the field-type-bound derive model, but that implementation is not in the current base:
thread_aware_macros_impl/src/lib.rs:121-134still addsThreadAwareto each reached generic parameter, and the linked #740 is still open/unmerged. Merging this documentation change independently would make the public guide disagree with the derive users receive; please coordinate the merge/dependency or keep this section aligned with the current implementation until #740 lands.
//! You rarely need to reason about this: the derive adds exactly the `ThreadAware` bounds its
//! generated body needs and no more, so a correct type "just derives". When it matters - a generic
//! wrapper, or a marker field that should stay bound-free - the derive's
//! [Generic Bounds](https://docs.rs/thread_aware/latest/thread_aware/derive.ThreadAware.html#generic-bounds)
//! reference has the rules.
crates/thread_aware/src/_documentation/mod.rs:213
- Although this section is presented as a test, the doctest only defines
assert_reaches_the_right_fields; it never calls the function withfromandto, so neither assertion is executed. Add an invocation with synthetic coordinates (or explicitly label this as a helper snippet) so the guide actually demonstrates a test that can fail when forwarding orskipbehavior regresses.
//! fn assert_reaches_the_right_fields(from: Option<&Thread>, to: &Thread) {
//! let mut value = UnderTest {
//! tracked: Tracker::default(),
//! skipped: Tracker::default(),
//! };
crates/thread_aware/src/_documentation/mod.rs:235
- The PR description lists a dedicated “Debugging & telemetry” section covering
*.thread_mismatch-style warnings and why their absence is not proof of correctness, but this module goes directly from# Testingto# Validating correctnessand contains none of that material. Please add the promised debugging guidance or remove it from the advertised contents so the published guide matches the scope described by the PR.
//! # Validating correctness
//!
//! What the toolchain checks for you, and what it cannot:
//!
//! * **The compiler** enforces the `ThreadAware: Send` supertrait and, through the derive's
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
| //! ```rust | ||
| //! use thread_aware::{Thread, ThreadAware}; | ||
| //! | ||
| //! #[derive(ThreadAware)] |
There was a problem hiding this comment.
Fixed in 180583e. Rather than shim each doctest, I gated the whole module on the derive feature: #[cfg(all(any(doc, test), feature = "derive"))]. Every example here is built around #[derive(ThreadAware)], so the guide is present exactly when the derive it documents is available (default and all-feature builds) and simply absent otherwise — which is what docs/feature-gated-doctests.md requires. Verified the guide doctests run under default features and are absent (no failure) under --no-default-features.
Addresses a Copilot review comment on PR #742: the guide's examples all use `#[derive(ThreadAware)]`, which is only re-exported with the `derive` feature, but the module was included for `cfg(any(doc, test))` even when that feature is off - so the doctests would not compile in a build without the optional macro. Add `feature = "derive"` to the module's cfg. The guide (and its doctests) is present exactly when the derive it documents is available - in the default and all-feature builds - and simply absent otherwise, which is what `docs/feature-gated-doctests.md` requires. Verified: guide doctests run under default features and are absent (no failure) under --no-default-features. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The guide contains API and behavior inaccuracies and lacks promised telemetry guidance.
Review details
Suppressed comments (8)
Previously missed (2) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:114
- The public strategy is named
performables::arc::PerNuma(crates/performables/src/arc/mod.rs:34-36), notPerNumaNode. As written, this identifier cannot be used with the current API.
crates/thread_aware/src/_documentation/mod.rs:240 - These bullets describe the field-type-bound behavior from #740, but the derive currently checked in still adds
ThreadAwarebounds to each reached generic parameter (crates/thread_aware_macros_impl/src/lib.rs:121-134) and has no field-type predicate/deduplication logic. Because #740 is a separate unmerged PR, this guide is inaccurate if the docs change lands independently; please make that dependency/atomic merge explicit or keep the guide aligned with the current derive.
crates/thread_aware/src/_documentation/mod.rs:144
ThreadAwareonly requiresSendandrelocate(crates/thread_aware_core/src/thread_aware.rs:146-161); it does not prescribe storage orClonesemantics. This paragraph overstates a migration-specific failure mode as a property of every thread-aware type and can mislead authors whoseCloneimplementation rebinds or recomputes state. Qualify it to clones that copy affinity-bearing state without rebinding.
//! This is the one to internalize first. A thread-aware type stores its affinity in a field that
//! only [`relocate`](crate::ThreadAware::relocate) mutates. **`Clone` copies that stored affinity
//! verbatim.** Cloning a value that was built on worker A and using the clone on worker B does not
//! move it to B - it is still bound to A, quietly, until something calls `relocate`.
crates/thread_aware/src/_documentation/mod.rs:101
- Because this example ignores
_destinationand only replaces the buffer withVec::new(), it does not select or guarantee a destination-NUMA allocation; whether a later allocation is local is allocator/runtime-dependent. The crate docs frame reallocation in a new NUMA region as a possible optimization (crates/thread_aware/src/lib.rs:27-37), so this comment should not present it as the result of this implementation.
//! // The scratch buffer belonged to the previous worker; drop it so the next use
//! // re-allocates in the destination's NUMA node instead of reaching across.
//! self.buffer = Vec::new();
crates/thread_aware/src/_documentation/mod.rs:111
- This presents strategy-partitioned
Arcas athread_awarefacility, butthread_awaredoes not exportArcor these strategies (crates/thread_aware/src/lib.rs:227-233); they are provided byperformables::arc(crates/performables/src/arc/mod.rs:21-36,150-170). Readers following this guide cannot obtain the shown type from the documented crate, so name the companion crate/dependency or remove this section.
//! pool you do not want contended across cores - wrap it in the strategy-partitioned `Arc<T, S>`
//! ([`thread_aware::Arc`](https://docs.rs/thread_aware/latest/thread_aware/struct.Arc.html), with
//! the crate's `std` feature). With the `PerThread` strategy,
crates/thread_aware/src/_documentation/mod.rs:112
- For the affinity-backed
Arc, relocation eagerly invokesfactory.materializebefore returning (crates/performables/src/arc/mod.rs:465-468); it is not deferred until the value is first used or dereferenced on the destination worker. Please describe this as materialization on the first relocation targeting that worker so callers do not underestimate relocation cost.
//! relocation materializes a separate `T` for the destination worker (lazily, on first use there),
crates/thread_aware/src/_documentation/mod.rs:101
- This manual implementation drops the buffer on every relocation and ignores the source/destination coordinates.
ThreadAwarepermits repeated calls, including when the destination is unchanged, and the core trait guidance recommends returning early and notes thatVeccannot select a NUMA node (crates/thread_aware_core/src/thread_aware.rs:70-73,108-120); as written, repeated calls needlessly discard reusable storage and the comment overstates NUMA placement.
//! fn relocate(&mut self, _source: Option<&Thread>, _destination: &Thread) {
//! // The scratch buffer belonged to the previous worker; drop it so the next use
//! // re-allocates in the destination's NUMA node instead of reaching across.
//! self.buffer = Vec::new();
crates/thread_aware/src/_documentation/mod.rs:233
- The PR description promises a “Debugging & telemetry” section covering
*.thread_mismatch-style warnings, but this module goes directly from# Testingto# Validating correctnessand never discusses that topic. Add the promised guidance or remove it from the stated contents so the documentation scope matches the change.
//! # Validating correctness
//!
//! What the toolchain checks for you, and what it cannot:
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Adds a `_documentation` module with a task-oriented guide for authors of thread-aware types, complementing the existing (reference-style) API docs. Covers what thread-awareness is and why it exists, how to author a type (derive, `#[thread_aware(skip)]`, hand-written impls, `Unaware`, strategy `Arc`), how to choose among them, how to test that relocation reaches the right fields, how to debug and read relocation telemetry, and how to validate correctness. The anti-patterns section folds in the migration experience of moving a large production service onto an Oxidizer runtime - `Clone` copying stored affinity rather than relocating, `#[thread_aware(skip)]` on a sole field silently no-op'ing, not trusting inherited markings, and relocating the whole dependency graph once at a boundary. Follows the `recoverable::_documentation` pattern. All examples are doctested under both default and all-feature configurations. Refs AB#7552151, AB#7722787. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`format_code_in_doc_comments` measures doc-comment code at the reduced width left by the `//! ` prefix, so the two `assert_eq!` calls in the testing example must wrap. Matches `cargo +nightly fmt --config-path ./unstable-rustfmt.toml`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review feedback on PR #742: export the `_documentation` module only for rustdoc and tests, matching the established pattern in `recoverable` and `fetch`, so it does not become part of the crate's public API in normal builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per @martintmk's review of PR #742: - Trim "Why thread-awareness exists" to a pointer at the crate-level Theory of Operation instead of duplicating it. - Link "the derive macro" to the actual macro (`macro@crate::ThreadAware`). - Simplify "What the generated bounds mean" - defer the detail to the derive's Generic Bounds reference rather than restating it. - Add a "Per-worker state with `Arc`" section explaining when to reach for `Arc<T, PerThread>` (separate per-worker instances) vs `PerProcess`/`PerNumaNode`. - Make "Testing" more concise and lead with the `test-utils` `Relocator` helper. - Drop "Debugging and telemetry" - the telemetry story is not defined yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e configs Addresses Copilot review comments on PR #742: - Use the `derive@` disambiguator for the derive-macro links (matches the repo convention, e.g. `internity`), replacing `macro@`. - The `Arc` strategy section pointed at `crate::Arc` / `crate::PerThread` / `crate::PerNumaNode`, which are `std`-gated, so the links broke under `--no-default-features`. Name the `std` feature and drop the feature-gated intra-doc links in favour of plain code spans. - `Relocator` is `test-utils`-gated; its intra-doc link broke in doc builds without that feature. Reword to a plain code span. Doctests still pass under default and all-feature builds; the guide no longer contributes any broken-intra-doc-link warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… guide Follow-up to 2ce36aa: the "Skipping a field" section still linked `[Arc](crate::Arc)`, which is `std`-gated and breaks under `--no-default-features`. Replace it with a plain code span that names the `std` feature, matching the treatment applied to the other `Arc` references. The guide now contributes no broken-intra-doc-link warnings in the `--no-default-features` doc build; doctests still pass under default and all-feature builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reference the facade types the guide mentions - the ThreadAware derive, Unaware, Arc, and Relocator - by docs.rs URL rather than intra-doc links, matching the pattern thread_aware_core already uses for these same types. This avoids a dev-dependency on thread_aware (and the dependency cycle it would create if the guide ever moves to thread_aware_core), and keeps every link resolvable regardless of which features the doc build enables - Arc is std-gated and Relocator is test-utils-gated, so intra-doc links to them broke under --no-default-features. Core types (the ThreadAware trait, Thread, relocate) stay as intra-doc links since they resolve in either crate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses a Copilot review comment on PR #742: the guide's examples all use `#[derive(ThreadAware)]`, which is only re-exported with the `derive` feature, but the module was included for `cfg(any(doc, test))` even when that feature is off - so the doctests would not compile in a build without the optional macro. Add `feature = "derive"` to the module's cfg. The guide (and its doctests) is present exactly when the derive it documents is available - in the default and all-feature builds - and simply absent otherwise, which is what `docs/feature-gated-doctests.md` requires. Verified: guide doctests run under default features and are absent (no failure) under --no-default-features. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
180583e to
5fddfd7
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The guide contains documentation inaccuracies and an unexecuted observation example that should be corrected.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (6)
crates/thread_aware/src/_documentation/mod.rs:114
PerNumaNodeis not the strategy exposed by this repository:performables/src/arc/mod.rs:34-36definesPerNuma, and its implementation is atperformables/src/arc/mod.rs:746-762. The guide therefore teaches an identifier users cannot use; please use the actualPerNumaname here.
//! vanilla `Arc`, when one shared instance is what you want, and `PerNumaNode` for one instance per
crates/thread_aware/src/_documentation/mod.rs:144
- The trait contract does not prescribe
Clonesemantics (thread_aware_core/src/thread_aware.rs:33-36explicitly says there are no exact semantic prescriptions), so a customCloneimplementation may reset or rebind affinity rather than copy it. Make this warning conditional on the type'sCloneimplementation copying its affinity-bearing state; otherwise the guide overgeneralizes a common pitfall into a trait rule.
//! This is the one to internalize first. A thread-aware type stores its affinity in a field that
//! only [`relocate`](crate::ThreadAware::relocate) mutates. **`Clone` copies that stored affinity
//! verbatim.** Cloning a value that was built on worker A and using the clone on worker B does not
//! move it to B - it is still bound to A, quietly, until something calls `relocate`.
crates/thread_aware/src/_documentation/mod.rs:209
- This doctest only defines
assert_reaches_the_right_fields; nothing in the block calls it, so rustdoc type-checks the assertions but never executes them. Because this section presents the snippet as the observation pattern, make the example invoke the helper (or turn it into an executable test with concreteThreadcoordinates).
//! fn assert_reaches_the_right_fields(from: Option<&Thread>, to: &Thread) {
crates/thread_aware/src/_documentation/mod.rs:112
PerThreaddoes not defer construction until first use:relocate_affinityinvokesfactory.materializewhile processingArc::relocate(crates/performables/src/arc/mod.rs:418-477), and the factory constructs the value immediately (crates/performables/src/arc/factory.rs:105-109). Calling this lazy gives authors the wrong relocation-cost and constructor-timing model; describe it as being created when relocation first encounters a destination partition.
//! relocation materializes a separate `T` for the destination worker (lazily, on first use there),
crates/thread_aware/src/_documentation/mod.rs:233
- The PR description promises a
Debugging & telemetrysection covering*.thread_mismatchwarnings and why their absence is not proof of correctness, but this module goes straight from# Testingto# Validating correctnessand contains no such guidance. Please add that promised section with the actual telemetry contract, or remove it from the stated contents.
//! # Validating correctness
//!
//! What the toolchain checks for you, and what it cannot:
crates/thread_aware/src/lib.rs:141
- The module is gated on
derive, but the guide contains a hand-written implementation example at lines 84-104 that does not use the derive macro. The explanation that every example is built around#[derive(ThreadAware)]is therefore inaccurate; qualify it as referring to the derive-based doctests instead of implying the manual section depends on the feature.
/// Gated on `derive` because every example is built around `#[derive(ThreadAware)]`, which is only
/// available with that feature; this keeps the guide's doctests valid in a build without it (they
/// are simply absent) per `docs/feature-gated-doctests.md`.
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
| //! // The scratch buffer belonged to the previous worker; drop it so the next use | ||
| //! // re-allocates in the destination's NUMA node instead of reaching across. | ||
| //! self.buffer = Vec::new(); |
There was a problem hiding this comment.
🔵 Needs a closer look
Documentation inaccuracies and a missing promised telemetry section remain unresolved.
Review details
Suppressed comments (5)
Previously missed (3) — in code that hasn't changed since the last review.
crates/thread_aware/src/_documentation/mod.rs:82
- This section documents the field-type-bound model from #740, but the derive present in this checkout still adds
ThreadAwareto each reached generic parameter incrates/thread_aware_macros_impl/src/lib.rs:121-133; the public derive reference likewise describes parameter bounds atcrates/thread_aware/src/lib.rs:173-176. Since #740 is currently unmerged, publishing this guide would claim support for cases the implementation does not provide. Merge the implementation and guide together, or describe the current derive behavior instead.
crates/thread_aware/src/_documentation/mod.rs:115 Arcis not a type inthread_aware: the strategy implementation lives in the separateperformables::arcmodule, and its NUMA strategy is namedPerNuma, notPerNumaNode(crates/performables/src/arc/mod.rs:26-36;thread_awarehas noperformablesdependency). As written, this section sends users to APIs that do not exist; point it atperformables::arcand usePerNuma(the table below repeats theArcshorthand).
crates/thread_aware/src/_documentation/mod.rs:233- The PR description promises a Debugging & telemetry section covering
*.thread_mismatch-style warnings and why their absence does not prove correctness, but this module contains no such section or guidance. Add that promised material or remove it from the stated contents so the authoring guide matches the PR scope.
crates/thread_aware/src/_documentation/mod.rs:100
Vecdoes not choose a NUMA node: dropping it only gives the application's allocator an opportunity to place a later allocation near the destination (crates/thread_aware_core/src/thread_aware.rs:115-120). Please qualify this example and make explicit that replacing the field is safe only for disposable scratch state, rather than promising reallocation in the destination node.
//! // The scratch buffer belonged to the previous worker; drop it so the next use
//! // re-allocates in the destination's NUMA node instead of reaching across.
crates/thread_aware/src/_documentation/mod.rs:144
- These sentences make copying affinity look like a
ThreadAware/Cloneguarantee, but the trait places no constraint onClone; an implementation may rebind state or not store affinity at all. Scope this warning to derived/fieldwiseClone(or types whose clone preserves the stored affinity) so the guide does not reject valid custom clone implementations.
//! This is the one to internalize first. A thread-aware type stores its affinity in a field that
//! only [`relocate`](crate::ThreadAware::relocate) mutates. **`Clone` copies that stored affinity
//! verbatim.** Cloning a value that was built on worker A and using the clone on worker B does not
//! move it to B - it is still bound to A, quietly, until something calls `relocate`.
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
What & why
Adds a
_documentationmodule tothread_awarewith a task-oriented authoring guide, the piece the crate''s (otherwise reference-style) docs are missing. Follows therecoverable::_documentationpattern and renders on docs.rs.Addresses 7552151 (publish the thread-aware authoring guide) and folds in 7722787 (the 3S / oxidizer-spawner migration experience).
Contents
#[thread_aware(skip)]; what the field-type bounds mean; hand-written impls.Unaware] vs. strategy [Arc], as a decision table.Clonecopies stored affinity instead of relocating;#[thread_aware(skip)]on a sole field is a silent no-op; don''t trust inherited markings; relocate the whole graph once at the boundary.Trackerobservation pattern (assert relocation reaches non-skipped fields and not skipped ones).*.thread_mismatch-style warnings, and why their absence doesn''t prove correctness.Notes
Refs AB#7552151, AB#7722787.