Skip to content

Modeling Algorithms - fix unbounded self-interference: O(1) tangent-zone point access + a checkpointed breaker - #1386

Open
gsdali wants to merge 1 commit into
Open-Cascade-SAS:masterfrom
gsdali:fix/319-selfintersection-checkpoint-and-quadratic
Open

Modeling Algorithms - fix unbounded self-interference: O(1) tangent-zone point access + a checkpointed breaker#1386
gsdali wants to merge 1 commit into
Open-Cascade-SAS:masterfrom
gsdali:fix/319-selfintersection-checkpoint-and-quadratic

Conversation

@gsdali

@gsdali gsdali commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1385: BOPAlgo_ArgumentAnalyzer's self-interference check (SelfInterMode()) can run effectively unbounded on a degenerate self-intersecting surface.

  1. Intf_Interference::Insert called Intf_TangentZone::GetPoint(Index) repeatedly inside a nested comparison loop. GetPoint is O(n) per call (the backing NCollection_Sequence has no O(1) indexed access), so every comparison paid that cost again -- measured ~80% of runtime in NCollection_BaseSequence::Find on the artifact in BOPAlgo_ArgumentAnalyzer self-interference: unbounded runtime (no checkpoint) + O(n) tangent-zone lookup #1385. Intf_TangentZone::Points() now caches a true random-access array per zone (invalidated on any mutation): same comparisons, same result, O(1) lookup.

  2. The self-interference phase never polled its cooperative progress indicator below BOPAlgo_CheckerSI::CheckFaceSelfIntersection, so a caller's timeout could only fire between whole-face checks, not within one -- 619s CPU against a 30s deadline on the artifact in BOPAlgo_ArgumentAnalyzer self-interference: unbounded runtime (no checkpoint) + O(n) tangent-zone lookup #1385, never returning. Intf_Interference::SetBreaker (RAII-scoped via Intf_InterferenceBreakerScope) lets Insert() poll every 256 calls and abort by throwing Standard_Failure. Wired up in BOPAlgo_CheckerSI's self-intersect functor, only when execution is guaranteed single-threaded -- an exception thrown from a worker thread of OSD_Parallel::For's parallel path would be unsafe (std::terminate).

Verification

On the artifact linked in #1385: with both fixes, a 0.5s deadline returns in 0.547s and a 30s deadline returns in 30.1s (vs. 619s+/never on stock), with correct HasFaulty() results throughout (the 0.5s case correctly reports "not yet determined" rather than a false negative, since the breaker trips before any fault is recorded).

Locally (minimal-module RelWithDebInfo build, FoundationClasses+ModelingData+ModelingAlgorithms only):

  • New GTests (Intf_TangentZone_Test.cxx, Intf_Interference_Test.cxx): 8/8 pass.
  • Related existing suites (Intf_*, IntPatch_*, IntTools_*, BOPAlgo_*, BRepAlgoAPI_*): 76/76 pass.
  • Full OpenCascadeGTest: 7241/7249 pass. The 8 failures are all BRepMesh_DiscretAlgoFactoryTest (mesh discretization plugin self-registration), an artifact of this being a minimal-module local build -- unrelated to this change and not touched by it.

Test plan

  • clang-format --style=file applied to all changed files.
  • New GTests added and passing locally.
  • No regression on related existing GTests.
  • CI (opening as draft to check style/build/test results first, per CONTRIBUTING.md).

point access + a checkpointed breaker

Fixes Open-Cascade-SAS#1385.

1. Intf_Interference::Insert calls Intf_TangentZone::GetPoint(Index) inside
   a nested comparison loop. GetPoint is O(n) per call (the backing
   NCollection_Sequence has no O(1) indexed access), so every comparison
   paid that cost again -- measured ~80% of runtime in
   NCollection_BaseSequence::Find on a degenerate self-intersecting
   surface. Intf_TangentZone::Points() caches a true random-access array
   per zone (invalidated on any mutation): same comparisons, same result,
   O(1) lookup.

2. The self-interference phase never polled its cooperative progress
   indicator below BOPAlgo_CheckerSI::CheckFaceSelfIntersection, so a
   caller's timeout could only fire between whole-face checks, not within
   one -- on the artifact in Open-Cascade-SAS#1385, 619s CPU against a 30s deadline, never
   returning. Intf_Interference::SetBreaker (RAII-scoped via
   Intf_InterferenceBreakerScope) lets Insert() poll every 256 calls and
   abort by throwing Standard_Failure. Wired up in BOPAlgo_CheckerSI's
   self-intersect functor, only when execution is guaranteed
   single-threaded (an exception from a worker thread of
   OSD_Parallel::For's parallel path would be unsafe).

Verified on the artifact linked in Open-Cascade-SAS#1385: with both fixes, a 0.5s deadline
returns in 0.547s and a 30s deadline returns in 30.1s (vs. 619s+/never on
stock), with correct HasFaulty() results throughout. Zero regression on
existing self-intersection GTest cases plus 76 related
Intf_*/IntPatch_*/IntTools_*/BOPAlgo_*/BRepAlgoAPI_* tests.

New GTests: Intf_TangentZone_Test.cxx (Points() correctness and cache
invalidation), Intf_Interference_Test.cxx (breaker aborts Insert()
promptly; a non-tripping or absent breaker leaves behavior unchanged).
@gsdali
gsdali marked this pull request as ready for review July 20, 2026 11:10
gsdali added a commit to SecondMouseAU/OCCTSwift that referenced this pull request Jul 20, 2026
…aker patch

Local carry of Open-Cascade-SAS/OCCT#1386 (CI green, ready for review),
fixing the isSelfIntersecting hardTimeout: hang on pathological
self-interference artifacts. Xcframework rebuild + release to follow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dpasukhi
dpasukhi self-requested a review July 22, 2026 14:12
@dpasukhi dpasukhi added this to the Release 8.1 milestone Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

BOPAlgo_ArgumentAnalyzer self-interference: unbounded runtime (no checkpoint) + O(n) tangent-zone lookup

2 participants