@@ -106,8 +106,32 @@ impl Drop for OldDefragTestEnable {
106106 }
107107}
108108
109+ /// RAII *disable* for defrag on this thread, so the OFF arm is exercised
110+ /// deterministically rather than depending on the ambient environment.
111+ ///
112+ /// Without this the OFF state has no behavioural coverage at all: the value
113+ /// mapping is unit-tested, but nothing asserts that a disabled collector
114+ /// actually declines to select a page. That gap is what #7917 records.
115+ #[ cfg( test) ]
116+ pub ( crate ) struct OldDefragTestDisable ;
117+
118+ #[ cfg( test) ]
119+ impl OldDefragTestDisable {
120+ pub ( crate ) fn new ( ) -> Self {
121+ OLD_DEFRAG_TEST_OVERRIDE . with ( |c| c. set ( Some ( false ) ) ) ;
122+ OldDefragTestDisable
123+ }
124+ }
125+
126+ #[ cfg( test) ]
127+ impl Drop for OldDefragTestDisable {
128+ fn drop ( & mut self ) {
129+ OLD_DEFRAG_TEST_OVERRIDE . with ( |c| c. set ( None ) ) ;
130+ }
131+ }
132+
109133fn old_page_defrag_enabled_from_value ( value : Option < & str > ) -> bool {
110- ! matches ! ( value, Some ( "0 " ) | Some ( "off " ) | Some ( "false " ) )
134+ matches ! ( value, Some ( "1 " ) | Some ( "on " ) | Some ( "true " ) )
111135}
112136
113137fn old_page_defrag_enabled ( ) -> bool {
@@ -123,9 +147,23 @@ fn old_page_defrag_enabled() -> bool {
123147}
124148
125149pub ( super ) fn select_old_page_defrag_pages ( force : bool ) -> OldPageDefragSelection {
126- // #7876 restored the mutable-root contract for old movable addresses and
127- // made defrag the production default. Keep an explicit kill switch for
128- // field diagnosis and rollback without shipping a second binary.
150+ // #7876 restored the mutable-root contract for old movable addresses, and
151+ // #7913 shipped that restoration with defrag ON by default. The contract
152+ // work is sound and stays; the DEFAULT is what this reverts (#7917).
153+ //
154+ // #7876's own acceptance criteria said to "re-enable defrag only after the
155+ // reproducer and a dependency-scale stress corpus are clean". No such
156+ // corpus exists yet, and none of the 19 benchmark programs can produce a
157+ // candidate page: selection needs `dead_bytes >= live_bytes` on an old
158+ // page, which needs promote-then-die at scale. The retain family survives
159+ // at 999-1000 permille and the churn family promotes almost nothing, so
160+ // the suite yields neither a benefit signal nor a regression signal while
161+ // still inheriting the full old-address rewrite surface.
162+ //
163+ // So this is opt-in until a fragmentation workload exists that can
164+ // actually exercise it. When that lands, the losing arm gets DELETED
165+ // rather than left standing -- per CLAUDE.md, a mode that still exists is
166+ // a decision that has not been made.
129167 if !old_page_defrag_enabled ( ) {
130168 return OldPageDefragSelection :: default ( ) ;
131169 }
@@ -135,17 +173,100 @@ pub(super) fn select_old_page_defrag_pages(force: bool) -> OldPageDefragSelectio
135173
136174#[ cfg( test) ]
137175mod tests {
138- use super :: old_page_defrag_enabled_from_value;
176+ use super :: {
177+ old_page_defrag_enabled_from_value, select_old_page_defrag_pages, OldDefragTestDisable ,
178+ OldDefragTestEnable ,
179+ } ;
139180
140181 #[ test]
141- fn old_page_defrag_defaults_on_with_an_explicit_kill_switch ( ) {
142- assert ! ( old_page_defrag_enabled_from_value( None ) ) ;
182+ fn old_page_defrag_is_opt_in_via_perry_gc_old_defrag ( ) {
183+ // Unset means OFF: defrag is opt-in until a fragmentation workload
184+ // exists that can demonstrate it (#7917).
185+ assert ! ( !old_page_defrag_enabled_from_value( None ) ) ;
143186 assert ! ( old_page_defrag_enabled_from_value( Some ( "1" ) ) ) ;
144187 assert ! ( old_page_defrag_enabled_from_value( Some ( "on" ) ) ) ;
145188 assert ! ( old_page_defrag_enabled_from_value( Some ( "true" ) ) ) ;
146189 assert ! ( !old_page_defrag_enabled_from_value( Some ( "0" ) ) ) ;
147190 assert ! ( !old_page_defrag_enabled_from_value( Some ( "off" ) ) ) ;
148191 assert ! ( !old_page_defrag_enabled_from_value( Some ( "false" ) ) ) ;
149- assert ! ( old_page_defrag_enabled_from_value( Some ( "unexpected" ) ) ) ;
192+ // Anything unrecognised is OFF, so a typo cannot silently enable
193+ // old-generation relocation.
194+ assert ! ( !old_page_defrag_enabled_from_value( Some ( "unexpected" ) ) ) ;
195+ }
196+
197+ /// The OFF arm, asserted through the gated entry point rather than through
198+ /// the value mapping.
199+ ///
200+ /// This matters because `select_old_page_defrag_pages_from_snapshot` does
201+ /// NOT consult the knob — the gate lives only in
202+ /// `select_old_page_defrag_pages` — so every pre-existing selection test
203+ /// bypasses the switch entirely. Before this test the OFF state had no
204+ /// behavioural coverage at all (#7917).
205+ ///
206+ /// The observable is the page-meta snapshot counter rather than the
207+ /// returned selection, for two reasons. It needs no old-arena fixture, and
208+ /// more importantly an empty selection is **not** evidence on its own: a
209+ /// bare test process has no eligible old pages, so asserting only
210+ /// "disabled returns nothing" passes just as happily against a kill switch
211+ /// that does nothing at all. That is the gate-that-cannot-fail shape this
212+ /// codebase keeps re-learning, and the first version of this test walked
213+ /// straight into it.
214+ ///
215+ /// So the positive control is load-bearing: it proves the enabled path
216+ /// really does reach the snapshot, which is the thing the disabled path
217+ /// must then be shown to skip.
218+ ///
219+ /// It also pins the *placement* of the gate, not merely its effect: the
220+ /// short-circuit must happen before the O(old pages) snapshot, so a
221+ /// disabled collector pays nothing on every ordinary minor.
222+ #[ test]
223+ fn disabled_defrag_short_circuits_before_taking_a_page_snapshot ( ) {
224+ use crate :: arena:: old_page_meta_snapshot_calls_for_tests as snapshot_calls;
225+
226+ let before_enabled = snapshot_calls ( ) ;
227+ let enabled = {
228+ let _enable = OldDefragTestEnable :: new ( ) ;
229+ select_old_page_defrag_pages ( true )
230+ } ;
231+ let enabled_calls = snapshot_calls ( ) - before_enabled;
232+
233+ let before_disabled = snapshot_calls ( ) ;
234+ let disabled_forced = {
235+ let _disable = OldDefragTestDisable :: new ( ) ;
236+ select_old_page_defrag_pages ( true )
237+ } ;
238+ let disabled_unforced = {
239+ let _disable = OldDefragTestDisable :: new ( ) ;
240+ select_old_page_defrag_pages ( false )
241+ } ;
242+ let disabled_calls = snapshot_calls ( ) - before_disabled;
243+
244+ assert_eq ! (
245+ enabled_calls, 1 ,
246+ "positive control: enabled defrag must reach the page snapshot. If \
247+ this is 0 the assertions below prove nothing, because a switch \
248+ that never runs looks identical to one that correctly declines"
249+ ) ;
250+
251+ assert_eq ! (
252+ disabled_calls, 0 ,
253+ "the kill switch must short-circuit BEFORE the O(old pages) \
254+ snapshot, so a disabled collector pays nothing per minor"
255+ ) ;
256+
257+ // `force` bypasses the dead>=live ratio, so this also proves the gate
258+ // beats a forced selection rather than merely losing the ratio test.
259+ assert_eq ! ( disabled_forced. selected_pages, 0 ) ;
260+ assert_eq ! ( disabled_forced. candidate_pages, 0 ) ;
261+ assert ! ( disabled_forced. pages. is_empty( ) ) ;
262+ assert ! ( disabled_forced. page_order. is_empty( ) ) ;
263+ assert_eq ! ( disabled_forced. selected_live_bytes, 0 ) ;
264+ assert_eq ! ( disabled_forced. selected_reclaimable_bytes, 0 ) ;
265+ assert_eq ! ( disabled_unforced. selected_pages, 0 ) ;
266+ assert ! ( disabled_unforced. pages. is_empty( ) ) ;
267+
268+ // Sanity: the enabled arm returned a real (possibly empty) selection
269+ // rather than the disabled default, i.e. the two paths are distinct.
270+ let _ = enabled;
150271 }
151272}
0 commit comments