Skip to content

Commit 38129e5

Browse files
committed
fix(reentrancy): make GuardCount unforgeable, widen the local rustdoc gate
`GuardCount` was a fieldless unit struct, so any code able to name it could construct one -- and therefore drop one. `Drop` decrements the thread-local, so a stray `drop(GuardCount)` while a tracked guard was live took the count to zero, `assert_no_guards_held` then passed, and a genuine callback-under-lock went unreported. `saturating_sub` made that desync permanently silent. A private field means only this module can mint one, so the count moves only by acquiring and releasing a real guard. The module docs called it a unit struct; they now say zero-sized newtype. `check-local.nu`'s rustdoc gate matched only `unresolved link`, while `check-local.sh:95` -- what CI runs -- also matches `broken_intra_doc_links`. A diagnostic carrying the lint name but not that phrase passed locally and failed remotely, which is the specific way this gate has already wasted CI cycles on this branch series. Both patterns now, as the shell gate does. No behaviour change: the four `reentrancy::tests` still pass, including `assert_fires_while_a_guard_is_live`, which is the one that proves the counter still detects.
1 parent 9bc6dfd commit 38129e5

2 files changed

Lines changed: 14 additions & 5 deletions

File tree

crates/hiroz/src/reentrancy.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
//!
3232
//! # Cost
3333
//!
34-
//! Zero in release. [`GuardCount`](crate::reentrancy::GuardCount) is a unit
35-
//! struct whose constructor and `Drop` compile to nothing without
34+
//! Zero in release. [`GuardCount`](crate::reentrancy::GuardCount) is a
35+
//! zero-sized newtype whose constructor and `Drop` compile to nothing without
3636
//! `debug_assertions`, and
3737
//! [`assert_no_guards_held`](crate::reentrancy::assert_no_guards_held)
3838
//! expands to nothing. Tests and CI run in debug, which is where the assertion
@@ -57,15 +57,24 @@ thread_local! {
5757
}
5858

5959
/// RAII counter embedded in every tracked guard.
60+
///
61+
/// The private field is what makes the counter trustworthy. As a fieldless unit
62+
/// struct this was constructible — and therefore *droppable* — by any code that
63+
/// could name it, and `Drop` decrements the thread-local. A stray
64+
/// `drop(GuardCount)` while a tracked guard was live would take the count to
65+
/// zero, `assert_no_guards_held` would pass, and a genuine callback-under-lock
66+
/// would go unreported. `saturating_sub` guaranteed that desync was silent.
67+
/// Only this module can mint one now, so the count can only be moved by
68+
/// acquiring and releasing a real guard.
6069
#[derive(Debug)]
61-
pub struct GuardCount;
70+
pub struct GuardCount(());
6271

6372
impl GuardCount {
6473
#[inline(always)]
6574
fn new() -> Self {
6675
#[cfg(debug_assertions)]
6776
LIVE_GUARDS.with(|n| n.set(n.get() + 1));
68-
Self
77+
Self(())
6978
}
7079
}
7180

scripts/check-local.nu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def main [--suite: string = "full"] {
8383
{name: "hu clippy (check-hu)", cmd: "nu scripts/test-pure-rust.nu check-hu"},
8484
{name: "SHM tests (test-shm)", cmd: "nu scripts/test-pure-rust.nu test-shm"},
8585
{name: "Distro feature flags (check-distro-features)", cmd: "nu scripts/test-pure-rust.nu check-distro-features"},
86-
{name: "Rustdoc links (cargo doc)", cmd: "let r = (^cargo doc --no-deps -p hiroz --quiet | complete); let w = ($r.stderr | lines | where $it =~ 'unresolved link'); if ($w | is-not-empty) { print ($w | str join (char newline)); error make {msg: 'rustdoc: unresolved intra-doc links'} }"},
86+
{name: "Rustdoc links (cargo doc)", cmd: "let r = (^cargo doc --no-deps -p hiroz --quiet | complete); let w = ($r.stderr | lines | where {|it| ($it =~ 'unresolved link') or ($it =~ 'broken_intra_doc_links')}); if ($w | is-not-empty) { print ($w | str join (char newline)); error make {msg: 'rustdoc: unresolved intra-doc links'} }"},
8787
]
8888

8989
let results = $checks | enumerate | each {|item|

0 commit comments

Comments
 (0)