1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Deterministic per-boolean exact-predicate budget (issue #1109).
//!
//! Every predicate over an implicit (intersection) point runs a cascade:
//! `interval filter → fixed-width exact → BigRational` (`predicates.rs`). The
//! interval filter resolves generic geometry for free, but on genuinely
//! near-coplanar / grazing operands its sign straddles zero, so the predicate
//! falls through to the exact tiers — the fixed-width rungs climb to ~1340 bits
//! and a few spill into BigRational (~5000× the interval tier). Boolean-heavy
//! CAD models (Tekla half-space end-clips, Revit flush openings) are full of
//! such faces, so a single hard element runs the exact path on a huge fraction
//! of the O(intersection-pairs) predicate set. The pure-Rust exact kernel has no
//! operand cap (the old BSP polygon cap was deleted in #1024), so those elements
//! grind to completion regardless of cost and stall the geometry stream at 95%
//! (issue #1109).
//!
//! This is a **deterministic** guardrail: it counts **interval-filter failures**
//! (every predicate that needed the expensive exact tier) per boolean and trips
//! when the count crosses a cap. The count is a pure function of the (snap-grid,
//! integer) operands, so the trip point is IDENTICAL on native x86_64 / aarch64
//! and on wasm32 — the server and the browser client degrade the SAME hard
//! element to the SAME fallback. That preserves the cross-target parity the
//! pure-Rust kernel exists to guarantee.
//!
//! A wall-clock budget would NOT preserve parity: the fast native server would
//! finish the exact cut while the slower wasm client tripped the timer, yielding
//! different geometry for the same model. The exact-evaluation COUNT is the right
//! metric precisely because it is platform-independent.
//!
//! On trip the boolean bails; [`crate::csg`] records `OperandTooLarge` and
//! returns the host un-cut, which routes void subtraction to the deterministic
//! #635 AABB box-cut fallback ("a square hole is dramatically less wrong than a
//! missing void").
use Cell;
use ;
/// Serialises every test that mutates the process-global `CAP` / `ELEMENT_CAP`
/// atomics, so cargo's parallel runner can't race them. Shared crate-wide (not
/// just this module's tests) so cap-mutating tests in OTHER modules — e.g. the
/// voids-router engulf-suppression regression — take the SAME lock and never
/// clobber each other's cap while a boolean runs. Test-only.
pub static GLOBAL_CAP_LOCK: Mutex = new;
/// Default per-boolean exact-evaluation cap for the interactive profile.
///
/// Calibrated against the model corpus: the worst healthy boolean (a dense steel
/// element) needs ~15k exact evaluations; almost all are well under 5k. This cap
/// is **33× that worst case**, so it never false-trips a legitimate cut (a false
/// trip degrades a real cut to an AABB box — wrong geometry — which is worse than
/// finishing slowly). It only engages on the pathological near-coplanar pattern
/// of #1109 (Tekla half-space clips / Revit flush cuts), where the exact-tier
/// count runs orders of magnitude higher and would otherwise never finish. The
/// coplanar fast path (the companion perf fix) keeps hard-but-sound models well
/// under this cap; this is the safety net that turns the indefinite 95% hang
/// into a finite load. `set_cap` (or `IFC_LITE_CSG_BUDGET`) tunes it;
/// `set_cap(None)` lifts it entirely for the server/offline-export profile,
/// where "exact but slow" is acceptable.
pub const DEFAULT_CAP: u64 = 500_000;
/// Default per-ELEMENT exact-evaluation cap (#1109 follow-up).
///
/// The per-boolean cap above bounds a SINGLE boolean, but it left two holes that
/// kept boolean-heavy models stalling at 95% even after the per-boolean budget
/// shipped:
/// 1. **Distributed cost.** An element with many openings (a 24-opening slab,
/// a window-dense facade) runs one boolean PER opening, each well under the
/// per-boolean cap, so none trips — yet the element's TOTAL exact work is
/// huge and the geometry batch blows the stream watchdog.
/// 2. **Overshoot.** A single heavily-fragmented host face retriangulates all
/// its constraint points in ONE `triangulate` call between two per-triangle
/// `tripped()` checks, so one boolean ran to ~1.7M escalations (3.3× a 500k
/// cap) before bailing — seconds of work past the cap.
///
/// This cap counts escalations across the WHOLE element (every boolean it runs)
/// and trips once the element total crosses it, so a hard element degrades as a
/// UNIT (its remaining cuts bail to the #635 AABB box-cut) instead of grinding.
/// It is still a deterministic COUNT, so native and wasm degrade the SAME element
/// identically (parity). Calibrated on the model corpus (the `csg_model_profile`
/// harness): healthy per-element totals are p50=0, p95≈150, p99≈13k — so this cap
/// is ~8× the 99th-percentile healthy element and never false-trips a legitimate
/// cut, while the pathological slabs that hung the stream (one needed 7.7M
/// escalations → 4 MINUTES) all sit far above it. At the measured ~50-60k
/// escalations/sec it bounds a hard element to ~2 s of exact work before its
/// remaining cuts degrade. The per-element cap only engages when [`begin_element`]
/// is called (the unified batch path); direct kernel / router / server /
/// offline-export callers that never open an element scope stay unbounded,
/// exactly as before — so the pinned kernel snapshots are unchanged.
pub const DEFAULT_ELEMENT_CAP: u64 = 100_000;
/// Global per-boolean cap. `0` ⇒ unbounded (run exact to completion). Any other
/// value is the per-boolean escalation cap. Read once per boolean into a
/// thread-local so each rayon worker counts its own operation independently and
/// deterministically.
static CAP: AtomicU64 = new;
/// Global per-element cap. `0` ⇒ unbounded. Snapshotted by [`begin_element`].
static ELEMENT_CAP: AtomicU64 = new;
/// Highest single-boolean escalation count seen since the last [`reset_peak`].
/// Diagnostics / cap calibration only — never read on the hot path.
static PEAK: AtomicU64 = new;
/// Highest single-ELEMENT escalation total seen since the last [`reset_peak`].
static ELEM_PEAK: AtomicU64 = new;
thread_local!
/// Effective cap, honouring the `IFC_LITE_CSG_BUDGET` env override (read once):
/// `0` ⇒ unbounded, any other value ⇒ that cap. Lets the server/CLI and
/// calibration runs pick a profile without code changes. `set_cap` still wins.
/// Per-element env override (`IFC_LITE_CSG_ELEMENT_BUDGET`, read once): `0` ⇒
/// unbounded, any other value ⇒ that cap. For calibration runs.
/// Set the global per-boolean escalation cap. `None` = unbounded (exact to
/// completion — the server/CLI/offline-export profile); `Some(n)` = trip after
/// `n` BigRational escalations (the interactive viewer/wasm profile). The
/// default is [`DEFAULT_CAP`], so the viewer is bounded out of the box.
/// Set the global per-element escalation cap (#1109 follow-up). `None` =
/// unbounded. The default is [`DEFAULT_ELEMENT_CAP`].
/// The active per-boolean cap, as configured (`None` ⇒ unbounded).
/// The active per-element cap, as configured (`None` ⇒ unbounded).
/// Begin a boolean operation: reset the per-op escalation counter and snapshot
/// the cap. Call once at every public boolean entry in [`crate::csg`].
/// Begin an ELEMENT scope (#1109 follow-up): reset the per-element accumulator
/// and snapshot the per-element cap. Call once per element at the unified
/// mesh-production entry (`ifc_lite_processing::element::produce_element_meshes`),
/// BEFORE its booleans run, so every boolean the element issues accumulates into
/// one budget and a hard element degrades as a whole.
///
/// The per-element cap is unbounded whenever the per-boolean profile is unbounded
/// (`set_cap(None)` / `IFC_LITE_CSG_BUDGET=0` — the server/offline-export
/// profile), so the SAME single switch keeps the server exact. Otherwise it is
/// the configured [`ELEMENT_CAP`] (or its `IFC_LITE_CSG_ELEMENT_BUDGET` override).
/// Highest single-boolean escalation count observed since process start (or the
/// last [`reset_peak`]). For cap calibration / diagnostics.
/// Highest single-ELEMENT escalation total observed since the last
/// [`reset_peak`]. For per-element cap calibration / diagnostics.
/// Reset the global peak escalation counters (per-boolean and per-element).
/// Record one exact-tier predicate evaluation (an interval-filter failure).
/// Called from the `.or_else(|| fixed::…)` arms of [`crate::kernel::predicates`]
/// — the point where a predicate leaves the cheap interval filter for the
/// expensive fixed-width / BigRational path.
///
/// DELIBERATE EXEMPTION — the cached-λ I512/I1024 tier is NOT counted. The
/// escalations tallied here all RE-DERIVE the degree-4/7 LPI/TPI lambda inside the
/// `fixed::indirect_*` / `orient2d_2i|3i` / `cmp_lex` arms of `predicates.rs`, and
/// that lambda recompute is the dominant per-escalation cost the caps were
/// calibrated against. The hot re-triangulation / arrangement predicates that run
/// straight off lambdas ALREADY interned — `retriangulate::orient2d_v` /
/// `cmp_lex_v` (their `fixed::orient2d_from_lam` / `cmp_lex_from_lam` determinant
/// branch, retriangulate.rs:51) and `arrangement::orient2d_end` (its cached
/// interval-lambda branch, arrangement/mod.rs:107) — skip that recompute, so they
/// are intentionally left OUT of the counter. Folding them in would inflate the
/// count on cheap cached evaluations and re-calibrate the effective cap, shifting
/// which elements trip and thereby perturbing valid mesh output / determinism; the
/// counter is kept as a proxy for the recompute-heavy work only.
/// Whether the current boolean OR the current element has exceeded its
/// escalation budget. Checked at loop boundaries in the arrangement AND inside
/// the per-point retriangulation loop so the bail is timely and graceful. Once
/// the per-element budget is blown, every subsequent boolean the element issues
/// trips immediately (its `COUNT` resets per [`begin`], but `ELEM_COUNT` does
/// not), so the element's remaining cuts bail to the #635 AABB fallback.
/// Escalations counted so far in the current boolean (diagnostics / cap
/// calibration).
/// Escalations accumulated so far in the current element across all its booleans
/// (diagnostics / per-element cap calibration).
/// Snapshot / restore the accumulators so a REFERENCE computation (`subtract_many`'s
/// volume-safe oracle) can't charge or trip the caller's #1109 batch budget (codex P2).
pub
pub