Skip to content

Commit

Permalink
Step thread safety improvement #307
Browse files Browse the repository at this point in the history
Mutex is added to XSControl_WorkSession to prevent data races
during reading and writing.
Tests are added to check the behavior of STEP readers/writers in
multithreading environment.
  • Loading branch information
AtheneNoctuaPt committed Jan 30, 2025
1 parent 2889518 commit 52c62ab
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 2 deletions.
177 changes: 177 additions & 0 deletions src/QABugs/QABugs_20.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@
#include <TDataStd_Name.hxx>
#include <AppCont_Function.hxx>
#include <math_ComputeKronrodPointsAndWeights.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <STEPCAFControl_Controller.hxx>
#include <ShapeAnalysis_ShapeContents.hxx>

#include <limits>

Expand Down Expand Up @@ -4923,6 +4926,155 @@ static Standard_Integer OCC33048(Draw_Interpretor&, Standard_Integer, const char
return 0;
}

//=================================================================================================

static Standard_Integer OCC33657_1(Draw_Interpretor&, Standard_Integer, const char**)
{
STEPCAFControl_Controller::Init();
// Checking constructors working in parallel.
OSD_Parallel::For(0, 1000, [](int) {
STEPCAFControl_Reader aReader;
aReader.SetColorMode(true);
STEPCAFControl_Writer aWriter;
aWriter.SetDimTolMode(true);
});

return 0;
}

//=================================================================================================

static Standard_Integer OCC33657_2(Draw_Interpretor& theDI,
Standard_Integer theArgC,
const char** theArgV)
{
if (theArgC < 2)
{
theDI << "Use: " << theArgV[0] << " file\n";
return 1;
}

STEPCAFControl_Controller::Init();
// Checking readers working in parallel.
OSD_Parallel::For(0, 100, [&](int) {
STEPControl_Reader aReader;
aReader.ReadFile(theArgV[1], DESTEP_Parameters{});
aReader.TransferRoots();
});

return 0;
}

//=================================================================================================

static Standard_Integer OCC33657_3(Draw_Interpretor&, Standard_Integer, const char**)
{
STEPCAFControl_Controller::Init();
const TopoDS_Shape aShape = BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape();
// Checking writers working in parallel.
OSD_Parallel::For(0, 100, [&](int) {
STEPControl_Writer aWriter;
aWriter.Transfer(aShape, STEPControl_StepModelType::STEPControl_AsIs, DESTEP_Parameters{});
std::ostringstream aStream;
aWriter.WriteStream(aStream);
});

return 0;
}

//=================================================================================================

static Standard_Integer OCC33657_4(Draw_Interpretor& theDI,
Standard_Integer theArgC,
const char** theArgV)
{
if (theArgC < 2)
{
theDI << "Use: " << theArgV[0] << " file\n";
return 1;
}

STEPCAFControl_Controller::Init();

// Acquire shape to write/read.
STEPControl_Reader aReader;
aReader.ReadFile(theArgV[1], DESTEP_Parameters{});
aReader.TransferRoots();
TopoDS_Shape aSourceShape = aReader.OneShape();

// Analyzer to compare the shape with the the same shape after write-read sequence.
ShapeAnalysis_ShapeContents aSourceAnalyzer;
aSourceAnalyzer.Perform(aSourceShape);

// Flag is set to false if any error is detected.
// Reads and writes to the flag are performed exclusively in relaxed memory order
// in order to avoid inter-thread syncronization that can potentially omit some problems.
std::atomic_bool anErrorOccurred(false);

OSD_Parallel::For(0, 100, [&](int) {
if (anErrorOccurred.load(std::memory_order_relaxed))
{
return;
}

// Writing.
STEPControl_Writer aWriter;
aWriter.Transfer(aSourceShape,
STEPControl_StepModelType::STEPControl_AsIs,
DESTEP_Parameters{});
std::stringstream aStream;
aWriter.WriteStream(aStream);

// Reading.
STEPControl_Reader aReader;
aReader.ReadStream("", DESTEP_Parameters{}, aStream);
aReader.TransferRoots();
const TopoDS_Shape aResultShape = aReader.OneShape();
ShapeAnalysis_ShapeContents aResultAnalyzer;
aResultAnalyzer.Perform(aResultShape);

// Making sure that shape is unchanged.
if (aSourceAnalyzer.NbSolids() != aResultAnalyzer.NbSolids())
{
theDI << "Error: Wrong number of solids in the result shape.\nExpected: "
<< aSourceAnalyzer.NbSolids() << "\nActual" << aResultAnalyzer.NbSolids() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
if (aSourceAnalyzer.NbShells() != aResultAnalyzer.NbShells())
{
theDI << "Error: Wrong number of shells in the result shape.\nExpected: "
<< aSourceAnalyzer.NbShells() << "\nActual" << aResultAnalyzer.NbShells() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
if (aSourceAnalyzer.NbFaces() != aResultAnalyzer.NbFaces())
{
theDI << "Error: Wrong number of faces in the result shape.\nExpected: "
<< aSourceAnalyzer.NbFaces() << "\nActual" << aResultAnalyzer.NbFaces() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
if (aSourceAnalyzer.NbWires() != aResultAnalyzer.NbWires())
{
theDI << "Error: Wrong number of wires in the result shape.\nExpected: "
<< aSourceAnalyzer.NbWires() << "\nActual" << aResultAnalyzer.NbWires() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
if (aSourceAnalyzer.NbEdges() != aResultAnalyzer.NbEdges())
{
theDI << "Error: Wrong number of edges in the result shape.\nExpected: "
<< aSourceAnalyzer.NbEdges() << "\nActual" << aResultAnalyzer.NbEdges() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
if (aSourceAnalyzer.NbVertices() != aResultAnalyzer.NbVertices())
{
theDI << "Error: Wrong number of vertices in the result shape.\nExpected: "
<< aSourceAnalyzer.NbVertices() << "\nActual" << aResultAnalyzer.NbVertices() << "\n";
anErrorOccurred.store(true, std::memory_order_relaxed);
}
});

return anErrorOccurred;
}

//=======================================================================
// function : QACheckBends
// purpose :
Expand Down Expand Up @@ -5283,5 +5435,30 @@ void QABugs::Commands_20(Draw_Interpretor& theCommands)
OCC26441,
group);

theCommands.Add(
"OCC33657_1",
"Check performance of STEPCAFControl_Reader/Writer constructors in multithreading environment.",
__FILE__,
OCC33657_1,
group);

theCommands.Add("OCC33657_2",
"Check performance of STEPControl_Reader in multithreading environment.",
__FILE__,
OCC33657_2,
group);

theCommands.Add("OCC33657_3",
"Check performance of STEPControl_Writer in multithreading environment.",
__FILE__,
OCC33657_3,
group);

theCommands.Add("OCC33657_4",
"Check performance of STEPControl_Reader/Writer in multithreading environment.",
__FILE__,
OCC33657_4,
group);

return;
}
9 changes: 8 additions & 1 deletion src/StepFile/StepFile_Read.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include <Standard_ErrorHandler.hxx>
#include <Standard_Failure.hxx>
#include <Standard_Mutex.hxx>

#include <Message.hxx>
#include <Message_Messenger.hxx>
Expand All @@ -46,6 +47,11 @@
#define CHRONOMESURE
#endif

namespace
{
static Standard_Mutex THE_GLOBAL_READ_MUTEX;
}

void StepFile_Interrupt(Standard_CString theErrorMessage, const Standard_Boolean theIsFail)
{
if (theErrorMessage == NULL)
Expand Down Expand Up @@ -113,7 +119,8 @@ static Standard_Integer StepFile_Read(const char* the

sout << " ... STEP File Read ...\n";

Standard_Integer nbhead, nbrec, nbpar;
Standard_Mutex::Sentry aLocker(THE_GLOBAL_READ_MUTEX);
Standard_Integer nbhead, nbrec, nbpar;
aFileDataModel.GetFileNbR(&nbhead, &nbrec, &nbpar); // renvoi par lex/yacc
Handle(StepData_StepReaderData) undirec =
// clang-format off
Expand Down
10 changes: 9 additions & 1 deletion src/XSControl/XSControl_WorkSession.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@

IMPLEMENT_STANDARD_RTTIEXT(XSControl_WorkSession, IFSelect_WorkSession)

namespace
{
static Standard_Mutex WS_GLOBAL_MUTEX; //!< Mutex to prevent data races during reading and writing.
}

//=================================================================================================

XSControl_WorkSession::XSControl_WorkSession()
Expand Down Expand Up @@ -67,6 +72,7 @@ void XSControl_WorkSession::ClearData(const Standard_Integer mode)

Standard_Boolean XSControl_WorkSession::SelectNorm(const Standard_CString normname)
{
const Standard_Mutex::Sentry aMutexLock(WS_GLOBAL_MUTEX);
// Old norm and results
myTransferReader->Clear(-1);
// ???? En toute rigueur, menage a faire dans XWS : virer les items
Expand Down Expand Up @@ -424,6 +430,7 @@ Standard_Integer XSControl_WorkSession::TransferReadRoots(const Message_Progress

Handle(Interface_InterfaceModel) XSControl_WorkSession::NewModel()
{
const Standard_Mutex::Sentry aMutexLock(WS_GLOBAL_MUTEX);
Handle(Interface_InterfaceModel) newmod;
if (myController.IsNull())
return newmod;
Expand All @@ -446,7 +453,8 @@ IFSelect_ReturnStatus XSControl_WorkSession::TransferWriteShape(
const Standard_Boolean compgraph,
const Message_ProgressRange& theProgress)
{
IFSelect_ReturnStatus status;
const Standard_Mutex::Sentry aMutexLock(WS_GLOBAL_MUTEX);
IFSelect_ReturnStatus status;
if (myController.IsNull())
return IFSelect_RetError;
const Handle(Interface_InterfaceModel)& model = Model();
Expand Down
1 change: 1 addition & 0 deletions src/XSControl/XSControl_WorkSession.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ private:
//! Clears binders
Standard_EXPORT void ClearBinders();

private:
Handle(XSControl_Controller) myController;
Handle(XSControl_TransferReader) myTransferReader;
Handle(XSControl_TransferWriter) myTransferWriter;
Expand Down
1 change: 1 addition & 0 deletions tests/bugs/step/begin
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pload XDE
pload QAcommands

set subgroup step

Expand Down
3 changes: 3 additions & 0 deletions tests/bugs/step/bug33657_1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Check performance of STEPCAFControl_Reader/Writer constructors in multithreading environment.
# If no crash occures, its fine.
OCC33657_1
3 changes: 3 additions & 0 deletions tests/bugs/step/bug33657_2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Check performance of STEPControl_Reader in multithreading environment.
# If no crash occures, its fine.
OCC33657_2 [locate_data_file bug21802_as1-oc-214.stp]
3 changes: 3 additions & 0 deletions tests/bugs/step/bug33657_3
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Check performance of STEPControl_Writer in multithreading environment.
# If no crash occures, its fine.
OCC33657_1
2 changes: 2 additions & 0 deletions tests/bugs/step/bug33657_4
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Check performance of STEPControl_Reader/Writer in multithreading environment.
OCC33657_4 [locate_data_file bug21802_as1-oc-214.stp]

0 comments on commit 52c62ab

Please sign in to comment.