Foundation, Modeling - Fix thread-safety data races for concurrent operations (#1180)

- Refactors `BRepCheck_*` result classes to use an always-present mutex with a parallel-mode guard, and updates the parallel analyzer to use the new locking model.
- Makes multiple Foundation-level globals thread-safe via `std::atomic`, adds mutex-based protection for lazy initialization, and introduces `std::call_once` on Windows host initialization.
- Converts several TKBool global mutable statics to `thread_local` to prevent cross-thread state corruption.
This commit is contained in:
Pasukhin Dmitry
2026-04-02 17:21:35 +01:00
committed by GitHub
parent ba824c84ef
commit 8d2d8650ca
27 changed files with 360 additions and 206 deletions
@@ -170,10 +170,11 @@ public:
if (performwire)
{
std::unique_lock<std::mutex> aLock =
aFaceEdgeRes->GetMutex()
? std::unique_lock<std::mutex>(*aFaceEdgeRes->GetMutex())
: std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(aFaceEdgeRes->myMutex, std::defer_lock);
if (aFaceEdgeRes->IsParallel())
{
aLock.lock();
}
if (aFaceEdgeRes->IsStatusOnShape(aShape))
{
NCollection_List<BRepCheck_Status>::Iterator itl(
@@ -219,9 +220,11 @@ public:
if (orientofwires)
{
std::unique_lock<std::mutex> aLock =
aFaceWireRes->GetMutex() ? std::unique_lock<std::mutex>(*aFaceWireRes->GetMutex())
: std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(aFaceWireRes->myMutex, std::defer_lock);
if (aFaceWireRes->IsParallel())
{
aLock.lock();
}
if (aFaceWireRes->IsStatusOnShape(aShape))
{
const NCollection_List<BRepCheck_Status>& aStatusList =
@@ -264,8 +264,11 @@ void BRepCheck_Edge::InContext(const TopoDS_Shape& S)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
if (myMap.IsBound(S))
{
return;
@@ -334,7 +337,7 @@ void BRepCheck_Edge::InContext(const TopoDS_Shape& S)
NCollection_List<occ::handle<BRep_CurveRepresentation>>::Iterator itcr(TE->Curves());
constexpr double eps = Precision::PConfusion();
const bool toRunParallel = myMutex != nullptr;
const bool toRunParallel = myIsParallel;
while (itcr.More())
{
const occ::handle<BRep_CurveRepresentation>& cr = itcr.Value();
@@ -578,8 +581,11 @@ bool BRepCheck_Edge::GeometricControls() const
void BRepCheck_Edge::SetStatus(const BRepCheck_Status theStatus)
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
BRepCheck::Add(*myMap(myShape), theStatus);
}
@@ -117,8 +117,11 @@ void BRepCheck_Face::InContext(const TopoDS_Shape& S)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
if (myMap.IsBound(S))
{
return;
@@ -167,8 +170,11 @@ BRepCheck_Status BRepCheck_Face::IntersectWires(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
@@ -297,8 +303,11 @@ BRepCheck_Status BRepCheck_Face::ClassifyWires(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
@@ -429,8 +438,11 @@ BRepCheck_Status BRepCheck_Face::OrientationOfWires(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
@@ -546,8 +558,11 @@ BRepCheck_Status BRepCheck_Face::OrientationOfWires(const bool Update)
void BRepCheck_Face::SetUnorientable()
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
BRepCheck::Add(*myMap(myShape), BRepCheck_UnorientableShape);
}
@@ -555,8 +570,11 @@ void BRepCheck_Face::SetUnorientable()
void BRepCheck_Face::SetStatus(const BRepCheck_Status theStatus)
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
BRepCheck::Add(*myMap(myShape), theStatus);
}
@@ -45,8 +45,11 @@ void BRepCheck_Result::Init(const TopoDS_Shape& S)
void BRepCheck_Result::SetFailStatus(const TopoDS_Shape& S)
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aList;
if (!myMap.Find(S, aList))
{
@@ -79,13 +82,3 @@ void BRepCheck_Result::NextShapeInContext()
myIter.Next();
}
}
//=================================================================================================
void BRepCheck_Result::SetParallel(bool theIsParallel)
{
if (theIsParallel && !myMutex)
{
myMutex = opencascade::make_unique<std::mutex>();
}
}
@@ -26,8 +26,6 @@
#include <NCollection_Shared.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_DataMap.hxx>
#include <Standard_MemoryUtils.hxx>
#include <mutex>
class BRepCheck_Result : public Standard_Transient
@@ -60,7 +58,11 @@ public:
Standard_EXPORT void NextShapeInContext();
Standard_EXPORT void SetParallel(bool theIsParallel);
//! Sets the parallel execution flag for sub-algorithms.
void SetParallel(const bool theIsParallel) { myIsParallel = theIsParallel; }
//! Returns TRUE if sub-algorithms should use parallel execution.
bool IsParallel() const { return myIsParallel; }
bool IsStatusOnShape(const TopoDS_Shape& theShape) const { return myMap.IsBound(theShape); }
@@ -80,14 +82,12 @@ protected:
TopoDS_Shape myShape;
bool myMin;
bool myBlind;
bool myIsParallel = false;
NCollection_DataMap<TopoDS_Shape,
Handle(NCollection_Shared<NCollection_List<BRepCheck_Status>>),
TopTools_ShapeMapHasher>
myMap;
mutable std::unique_ptr<std::mutex> myMutex;
private:
std::unique_ptr<std::mutex>& GetMutex() { return myMutex; }
myMap;
mutable std::mutex myMutex;
private:
NCollection_DataMap<TopoDS_Shape,
@@ -183,8 +183,11 @@ void BRepCheck_Shell::InContext(const TopoDS_Shape& S)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
if (myMap.IsBound(S))
{
return;
@@ -255,8 +258,11 @@ BRepCheck_Status BRepCheck_Shell::Closed(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
@@ -438,8 +444,11 @@ BRepCheck_Status BRepCheck_Shell::Orientation(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
NCollection_List<BRepCheck_Status>& aStatusList = *aHList;
@@ -837,8 +846,11 @@ BRepCheck_Status BRepCheck_Shell::Orientation(const bool Update)
void BRepCheck_Shell::SetUnorientable()
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
BRepCheck::Add(*myMap(myShape), BRepCheck_UnorientableShape);
}
@@ -853,8 +865,11 @@ bool BRepCheck_Shell::IsUnorientable() const
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
NCollection_List<BRepCheck_Status>& aStatusList = *aHList;
@@ -67,8 +67,11 @@ void BRepCheck_Vertex::InContext(const TopoDS_Shape& S)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
if (myMap.IsBound(S))
{
return;
@@ -193,8 +193,11 @@ void BRepCheck_Wire::InContext(const TopoDS_Shape& S)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
if (myMap.IsBound(S))
{
return;
@@ -281,8 +284,11 @@ BRepCheck_Status BRepCheck_Wire::Closed(const bool Update)
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
@@ -516,8 +522,11 @@ BRepCheck_Status BRepCheck_Wire::Closed2d(const TopoDS_Face& theFace, const bool
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
NCollection_List<BRepCheck_Status>& aStatusList = *aHList;
@@ -694,8 +703,11 @@ BRepCheck_Status BRepCheck_Wire::Orientation(const TopoDS_Face& F, const bool Up
BRepCheck_Status theOstat = Closed();
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
NCollection_List<BRepCheck_Status>& aStatusList = *aHList;
@@ -1036,8 +1048,11 @@ BRepCheck_Status BRepCheck_Wire::SelfIntersect(const TopoDS_Face& F,
{
occ::handle<NCollection_Shared<NCollection_List<BRepCheck_Status>>> aHList;
{
std::unique_lock<std::mutex> aLock =
myMutex ? std::unique_lock<std::mutex>(*myMutex) : std::unique_lock<std::mutex>();
std::unique_lock<std::mutex> aLock(myMutex, std::defer_lock);
if (myIsParallel)
{
aLock.lock();
}
aHList = myMap(myShape);
}
NCollection_List<BRepCheck_Status>& aStatusList = *aHList;