Modeling - Make fillet solid reconstruction reentrant across threads#1374
Open
gsdali wants to merge 1 commit into
Open
Modeling - Make fillet solid reconstruction reentrant across threads#1374gsdali wants to merge 1 commit into
gsdali wants to merge 1 commit into
Conversation
gsdali
added a commit
to SecondMouseAU/OCCTSwift
that referenced
this pull request
Jul 18, 2026
…n lock (#308) The real fix for #298 lands in the pinned OCCT, replacing the v1.12.1 bridge lock. Root cause (found with ThreadSanitizer): BRepFilletAPI_MakeFillet reconstructs its result solid via OCCT's legacy TopOpeBRepBuild engine, which passed state between methods through a file-scope static, STATIC_SOLIDINDEX (SplitSolid sets 1/2, FillSolid reads it back to pick the operand). Concurrent fillets on independent shapes clobbered each other's flag, mis-classifying faces -> wrong-but-plausible solid that fails BRepCheck. NOT the BlendFunc scratch the v1.12.1 notes suspected (those race benignly). - Scripts/patches/0003: converts the fillet-path statics to thread_local (STATIC_SOLIDINDEX + STATIC_lastVPind functional; BlendFunc_ConstRad/EvolRad and ChFi3d_Builder checkcurve scratch benign, for TSan-clean). Upstreamed as Open-Cascade-SAS/OCCT#1374. - xcframework rebuilt with 0003; Package.swift url+checksum bumped to v1.12.3. - occtFilletMutex and its 16 bridge guard sites removed — fillet/chamfer are reentrant in the kernel now, so concurrent builds run in parallel again. - Issue298FilletThreadSafetyTests kept; passes with the lock removed. Verified: pure-C++ 8-thread stress 0/1600 invalid (was ~15-20%), TSan-clean on the fillet path; the Swift regression + Modeling/Thread suites pass against the patched binary with the lock gone. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gsdali
force-pushed
the
fix/fillet-thread-safety-topopebrep-298
branch
from
July 18, 2026 02:58
c585c6a to
7729e7b
Compare
gsdali
marked this pull request as ready for review
July 18, 2026 02:59
BRepFilletAPI_MakeFillet reconstructs its result solid through the legacy TopOpeBRepBuild engine (ChFi3d_Builder::Compute -> TopOpeBRepBuild_HBuilder:: MergeSolid -> TopOpeBRepBuild_Builder::SplitSolid), which passes state between methods through file-scope static variables. That makes it non-reentrant: two BRepFilletAPI_MakeFillet builds on independent shapes on separate threads corrupt each other and yield a wrong-but-plausible solid that fails BRepCheck. ThreadSanitizer on an 8-thread fuse+fillet stress pinpoints the functional cause as STATIC_SOLIDINDEX (TopOpeBRepBuild_Builder.cxx): SplitSolid sets it to 1/2 to tell FillSolid which operand it is splitting, FillSolid reads it back. Concurrent reconstructions interleave the writes, so FillSolid mis-classifies faces and drops material. Converting that one variable to thread_local makes a 1600-build concurrent stress return correct geometry every time (was ~15-20% corrupt). Converts the fillet-path shared statics to thread_local (each thread keeps its own copy of the cross-call state; single-thread behaviour is unchanged): - Functional: TopOpeBRepBuild_Builder STATIC_SOLIDINDEX; TopOpeBRep_kpart STATIC_lastVPind (same cross-call-cache pattern). - Benign data races on the same path (no corruption, but flagged by TSan): BlendFunc_ConstRad / BlendFunc_EvolRad ComputeValues scratch; ChFi3d_Builder_6 checkcurve. Same class of fix, same engine, as the earlier TKBool thread_local conversion (Open-Cascade-SAS#1180); these were not covered by it. Reported downstream as SecondMouseAU/ OCCTSwift#298.
gsdali
force-pushed
the
fix/fillet-thread-safety-topopebrep-298
branch
from
July 18, 2026 03:02
7729e7b to
c10f48c
Compare
This was referenced Jul 18, 2026
Merged
Merged
Merged
dpasukhi
self-requested a review
July 18, 2026 07:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
BRepFilletAPI_MakeFilletis not reentrant: two builds on independent shapes running on separate threads — the concurrency model OCCT documents as supported ("algorithms on different threads in different contexts") — corrupt each other and return a wrong-but-plausible solid that failsBRepCheck. Silent bad geometry: no crash, no thrown error.The fillet reconstructs its result solid through the legacy
TopOpeBRepBuildengine:STATIC_SOLIDINDEX(TopOpeBRepBuild_Builder.cxx) is a file-scopestaticused as a mode flag:SplitSolidsets it to 1/2 to tellFillSolidwhich operand it is currently splitting, andFillSolidreads it back (if (STATIC_SOLIDINDEX == 1)) to pick the operand shape. Two concurrent reconstructions interleave the writes, soFillSolidreads the wrong value and mis-classifies faces — dropping material.Evidence (ThreadSanitizer + behaviour)
Isolated pure-C++ repro: fuse three prisms into a U-channel, fillet the seam, 8 threads. Built RelWithDebInfo +
-fsanitize=thread,MMGT_OPT=0.STATIC_SOLIDINDEX(write/write inSplitSolidfrom two threads), plus benign scratch races in the blend solver.Change
Converts the fillet-path shared statics to
static thread_local— each thread keeps its own copy of the cross-call state, so single-thread behaviour is identical while independent instances on separate threads become reentrant:TopOpeBRepBuild_Builder.cxxSTATIC_SOLIDINDEXTopOpeBRep_kpart.cxxSTATIC_lastVPindBlendFunc_ConstRad.cxx,BlendFunc_EvolRad.cxxComputeValuesscratchChFi3d_Builder_6.cxxcheckcurveThis is the same class of fix, in the same engine, as the earlier TKBool
thread_localconversion (#1180, 19 globals across 8 files);STATIC_SOLIDINDEXand these were not covered by it.Reported downstream as SecondMouseAU/OCCTSwift#298.
Draft while I confirm CI; happy to split the benign scratch conversions into a separate change if you'd prefer the functional
STATIC_SOLIDINDEXfix on its own.